Best objective-c questions in December 2011

Objective-C: format numbers to ordinals: 1, 2, 3, .. to 1st, 2nd, 3rd

13 votes

In Objective C, is there any way to format an integer to ordinals 1 => "1st", 2 => "2nd" etc... that works for any language? So if the user is French he will see "1er", "2ieme" etc..

Thanks a lot!

Edit: This is for an iOs app

Have you taken a look at TTTOrdinalNumberFormatter which is in FormatterKit? It works great, and I'm pretty sure it's exactly what you're looking for.


Here's an example taken from the kit:

TTTOrdinalNumberFormatter *ordinalNumberFormatter = [[TTTOrdinalNumberFormatter alloc] init];
[ordinalNumberFormatter setLocale:[NSLocale currentLocale]];
[ordinalNumberFormatter setGrammaticalGender:TTTOrdinalNumberFormatterMaleGender];
NSNumber *number = [NSNumber numberWithInteger:2];
NSLog(@"%@", [NSString stringWithFormat:NSLocalizedString(@"You came in %@ place!", nil), [ordinalNumberFormatter stringFromNumber:number]]);

Assuming you've provided localized strings for "You came in %@ place!", the output would be:

* English: "You came in 2nd place!"
* French: "Vous êtes venu à la 2eme place!"
* Spanish: "Usted llegó en 2.o lugar!"

Is it right having ViewControllers with a lots of code?

10 votes

I'm still quite new to Cocoa and Objective-C (<1 year). My App now has some 50+ classes, but some of the ViewControllers get quite crowded with code, like 700 lines or more.

My question is: is it fine to have a "large" ViewController or are there patterns for splitting up code into fractions? A lots of the code is implementing delegate methods, thats why I don't have an idea how to move it away.

I know, I can structurize with pragma marks though.

Thanks for any input.

One of the most common causes of large view controllers that I have seen, is lack of separation b/w Model and Controller in MVC architecture. In other words, are you handling your data in your view controllers?

If yes, rip out the model component from VC and put it into separate class. This will also force your thinking towards a better design.

For reference, In View Controller:

  • Handling of all changes in the UIView and the UI elements contained within.
  • All animation, transitions and CALayer operations.

In Model:

  • All processing of data, including sorting, conversion, storage, etc.

Why does [NSMutableString stringWithString:@""] work?

8 votes

Just wondering:

In NSString there is a static method called +stringWithString:. This is not redeclared/overridden in NSMutableString so we cannot assume that this will return an NSMutableString. In fact even in the NSString class the return type is defined as id and the doc states:

Return Value
A string created by copying the characters from aString.

What part of objective C am I missing in my knowledge to understand why this works and returns an NSMutableString? Especially because the base class NSString is not aware that we want a mutable string in return.

One could say that internally [Class alloc] is called which will generate an object of type NSMutableString, but even this is pure guesswork as we do not have the source code and stringWithString: could do whatever it wants internally.

Are all those class methods reimplemented in the subclass? And if yes why isn't this documented?

In NSString there is a static method called +stringWithString:.

more approriately, it's a class method.

This is not redeclared/overridden in NSMutableString

In cocoa, the subclass does not need to redeclare the method. In fact, it would just produce a lot of noise (IMO). It only needs to redefine the method to provide its custom implementation.

so we cannot assume that this will return an NSMutableString.

We must assume that it will return a mutable string. A subclass can redefine its initializers and convenience constructors as needed in order to meet the required contracts without publicly redeclaring the method -- it only needs to define the method when the base's implementation is insufficient.

What part of objective C am I missing in my knowledge to understand why this works and returns an NSMutableString? Especially because the base class NSString is not aware that we want a mutable string in return.

It 'knows' because you have written [NSMutableString stringWithString:@"bah"] rather than [NSString stringWithString:@"bah"]. Like instance methods, class methods have an implicit self which allows them to pass the type through class methods. Therefore, class methods may be redefined/overridden as needed. The class methods may also use self to determine or message their type (example shortly).

One could say that internally [Class alloc] is called which will generate an object of type NSMutableString, but even this is pure guesswork as we do not have the source code and stringWithString: could do whatever it wants internally.

It should not be guesswork. In this case, you should file a bug if you were returned an immutable string. Otherwise, it works as advertised, regardless of whether they used one or more definitions.

Are all those class methods reimplemented in the subclass?

In the case of convenience constructors, it's more common to go through one of the designated initializers:

such an implementation could take the form:

@implementation NSString
+ (id)stringWithString:(NSString *)arg
{
    // self is either +NSString or +NSMutableString
    return [[[self alloc] initWithString:arg] autorelease];
}

although exceptions can often be made, and is often the case with optimized immutable/mutable types:

@implementation NSString
+ (id)stringWithString:(NSString *)arg
{
  return [arg imp_isMutable] ? [[[self alloc] initWithString:arg] autorelease] : arg;
}
...
@implementation NSMutableString
+ (id)stringWithString:(NSString *)arg
{
  return [[[self alloc] initWithString:arg] autorelease];
}
...

And if yes why isn't this documented?

They should not be redeclared or redocumented when the only difference is the class type which you have requested, unless they have some deviation from the base class or special note -- even in that case, it would be better to create a method with another name.

Get an array of future NSDates

8 votes

I have a date picker.

After choosing a time from this I would like to get the dates of the next 64 Mondays.

How would I go about writing a method to take a date and return an NSArray of NSDates for the next 64 Mondays from that date

for e.g. I picked time 6:45 pm from date picker then I want to fetch next 64 mondays with there time set to that time.

Example (ARC):

NSDate *pickerDate = [NSDate date];
NSLog(@"pickerDate: %@", pickerDate);

NSDateComponents *dateComponents;
NSCalendar *calendar = [NSCalendar currentCalendar];

dateComponents = [calendar components:NSWeekdayCalendarUnit fromDate:pickerDate];
NSInteger firstMondayOrdinal = 9 - [dateComponents weekday];
dateComponents = [[NSDateComponents alloc] init];
[dateComponents setDay:firstMondayOrdinal];
NSDate *firstMondayDate = [calendar dateByAddingComponents:dateComponents toDate:pickerDate options:0];

dateComponents = [[NSDateComponents alloc] init];
[dateComponents setWeek:1];

for (int i=0; i<64; i++) {
    [dateComponents setWeek:i];
    NSDate *mondayDate = [calendar dateByAddingComponents:dateComponents toDate:firstMondayDate options:0];
    NSLog(@"week#: %i, mondayDate: %@", i, mondayDate);
}

NSLog output:
pickerDate: 2011-12-09 20:38:25 +0000
week#: 0, mondayDate: 2011-12-12 20:38:25 +0000
week#: 1, mondayDate: 2011-12-19 20:38:25 +0000
week#: 2, mondayDate: 2011-12-26 20:38:25 +0000
week#: 3, mondayDate: 2012-01-02 20:38:25 +0000
-the remaining 60 here-

What do you think about this code in Objective-C that iterates thorugh retain count and call release every iteration?

8 votes

I'm still trying to understand this piece of code that I found in a project I'm working on where the guy that created it left the company before I could ask.

This is the code:

-(void)releaseMySelf{
    for (int i=myRetainCount; i>1; i--) {
        [self release];
    }
    [self autorelease];
}

As far as I know, in Objective-C memory management model, the first rule is that the object that allocates another object, is also responsible to release it in the future. That's the reason I don't understand the meaning of this code. Is there is any meaning?

The author is trying to work around not understand memory management. He assumes that an object has a retain count that is increased by each retain and so tries to decrease it by calling that number of releases. Probably he has not implemented the "is also responsible to release it in the future." part of your understanding.

However see many answers here e.g. here and here and here.

Read Apple's memory management concepts.

The first link includes a quote from Apple

The retainCount method does not account for any pending autorelease messages sent to the receiver.

Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method. To understand the fundamental rules of memory management that you must abide by, read “Memory Management Rules”. To diagnose memory management problems, use a suitable tool: The LLVM/Clang Static analyzer can typically find memory management problems even before you run your program. The Object Alloc instrument in the Instruments application (see Instruments User Guide) can track object allocation and destruction. Shark (see Shark User Guide) also profiles memory allocations (amongst numerous other aspects of your program).

8 votes

I am a new cocoa developer coming from a C#/Java background. I had been introduced to memory management patterns which objective-c language uses and I just find them very helpful for code-conscious development.

Why does Apple now want us to use ARC (Automatic Reference Counting) instead of MRR (Manual Retain-Release) and what advantages other than time saving does ARC offer?

I see such a transition negatively affecting good-citizen habits that developers gain from the obj-c ecosystem.

Nick

It's unfortunate that making it easier for competent developers to be correct has a side effect of making it easier for new developers to not learn, but it seems like it's probably worth it.

ARC is less forgiving than a tracing collector like C# or Java use though. If you don't have a clear object ownership model you will almost certainly create cycles and leak tons of memory. My hope would be that this is made obvious enough (via Instruments? That still requires seeking it out... not sure what could be done here) that new developers will quickly learn to keep their object graph clear and acyclic.

Sort Colors (Objective-C)

8 votes

I'm doing this sort of thing:

- (NSArray*)colors {
    float divisor = .3333;
    NSMutableArray *retVal = [NSMutableArray array];
    for (float one=0; one <= 1.0f; one += divisor) {
        for (float two = 0; two <= 1.0f; two += divisor) {
            for (float three = 0; three <= 1.0f; three += divisor) {
                UIColor *color = [UIColor colorWithRed:one green:two blue:three alpha:.5];
                // also bad
                // UIColor *color = [UIColor colorWithHue:one saturation:two brightness:three alpha:.5];
                [retVal addObject:color];
            }
        }
    }
    return retVal;
}

and, as I suspected, the colors come out horribly out of order (to the eye). The reds are not with the reds, purples not with the purples, etc.

Is there no easy way to create a list of diverse colors, nicely grouped according to human criteria like, "that looks blue?"

This worked quite well. It will NOT help the fact that you have a lot of repeated colors. See below:

NSArray *sorted = [[dict allValues] sortedArrayUsingComparator:^NSComparisonResult(UIColor* obj1, UIColor* obj2) {
    float hue, saturation, brightness, alpha;
    [obj1 getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];
    float hue2, saturation2, brightness2, alpha2;
    [obj2 getHue:&hue2 saturation:&saturation2 brightness:&brightness2 alpha:&alpha2];
    if (hue < hue2)
        return NSOrderedAscending;
    else if (hue > hue2)
        return NSOrderedDescending;

    if (saturation < saturation2)
        return NSOrderedAscending;
    else if (saturation > saturation2)
        return NSOrderedDescending;

    if (brightness < brightness2)
        return NSOrderedAscending;
    else if (brightness > brightness2)
        return NSOrderedDescending;

    return NSOrderedSame;
}];

You can access the components (HSBA) like this in iOS 4.x:

    CGFloat *components = (CGFloat *)CGColorGetComponents([color CGColor]);
    float hue = components[0];
    float saturation = components[1]; // etc. etc.

To avoid repeating colors: you can put the elements in an NSMutableDictionary, keyed on something like their hue-saturation-brightness (each rounded to the nearest .10)... then you get the array from THAT, and then sort.

iOS Stream Audio from one iOS Device to Another

7 votes

I get a song from the device iTunes library and shove it into an AVAsset:

- (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
    NSArray *arr = mediaItemCollection.items;

    MPMediaItem *song = [arr objectAtIndex:0];

    NSData *songData = [NSData dataWithContentsOfURL:[song valueForProperty:MPMediaItemPropertyAssetURL]];
}

Then I have this Game Center method for receiving data:

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID

I'm having a LOT of trouble figuring out how to send this AVAsset via GameCenter and then have it play on the receiving device.

I've read through: http://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioStreamReference/Reference/reference.html#//apple_ref/doc/uid/TP40006162

http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html#//apple_ref/doc/uid/TP40009767-CH2-SW5

http://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html

http://developer.apple.com/library/mac/#documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/Introduction/Introduction.html

I am just lost. Information overload.

I've implemented Cocoa With Love's Audio Stream code, but I can't figure out how to take the NSData I receive through GameCenter and shove it into his code. http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html

Can someone please help me figure this out? So again the part I need help with is simply breaking up song data into packets (or however it works), then iterating through those packets and sending it through gamekit, then parsing that data AS it comes in on the receiving device as PLAY it AS it comes in.

The API you need to look at is "Audio Queue Services".

Right, here goes for a basic overview of what you need to do.

When you playback audio, you set up a queue or a service. That queue will ask for some audio data. Then, when it has played all that back, it will ask for some more. This goes on until you either stop the queue, or there's no more data to playback.

The two main lower level APIs in iOS are Audio Unit and Audio Queue. By lower level, I mean an API that is a bit more nitty gritty than saying "just play back this mp3" or whatever.

My experience has been that Audio Unit is lower latency, but that Audio Queue is more suited to streaming audio. So, I think for you the latter is a better option.

A key part of what you need to do is buffering. That means loading data sufficiently so that there are no gaps in your playback. You might want to handle this by initially loading a larger amount of data. Then you are playing ahead. You'll have a sufficiently large buffer in memory whilst simultaneously receiving more data on a background thread.

The sample project I would recommend studying closely is SpeakHere. In particular look at the classes SpeakHereController.mm and AQPlayer.mm.

The controller handles things like starting and stopping AQPlayer. AQPlayer represents an AudioQueue. Look closely at AQPlayer::AQBufferCallback. That's the callback method that is invoked when the queue wants more data.

You'll need to make sure that the set up of the queue data, and the format of the data you receive matches exactly. Checkout things like number of channels (mono or stereo?), number of frames, integers or floats, and sample rate. If anything doesn't match up, you'll either get EXC_BAD_ACCESS errors as you work your way through the respective buffers, or you'll get white noise, or - in the case of wrong sample rates - audio that sounds slowed down or sped up.

Note that SpeakHere runs two audio queues; one for recording, and one for playback. All audio stuff works using buffers of data. So you're always passing round pointers to the buffers. So, for example during playback you will have say a memory buffer that has 20 seconds of audio. Perhaps every second your callback will be invoked by the queue, essentially saying "give me another second's worth of data please". You could think of it as a playback head that moves through your data requesting more information.

Let's look at this in a bit more detail. Differently to SpeakHere, you're going to be working with in memory buffers rather than writing out the audio to a temporary file.

Note that if you're dealing with large amounts of data, on an iOS device, you'll have no choice but to hold the bulk of that on disk. Especially if the user can replay the audio, rewind it, etc., you'll need to hold it all somewhere!

Anyway, assuming that AQPlayer will be reading from memory, we'll need to alter it as follows.

First, somewhere to hold the data, in AQPlayer.h:

void SetAudioBuffer(float *inAudioBuffer) { mMyAudioBuffer = inAudioBuffer; }

You already have that data in an NSData object, so you can just pass in the pointer returned from a call to [myData bytes].

What provides that data to the audio queue? That's the call back method set up in AQPlayer:

void AQPlayer::AQBufferCallback(void *                  inUserData,
                            AudioQueueRef           inAQ,
                            AudioQueueBufferRef     inCompleteAQBuffer) 

The method that we'll use to add part of our data to the audio queue is AudioQueueEnqueueBuffer:

AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL);

inAQ is the reference to the queue as received by our callback. inCompleteAQBuffer is the pointer to an audio queue buffer.

So how do you get your data - that is the pointer returned by calling the bytes method on your NSData object - into the audio queue buffer inCompleteAQBuffer?

Using a memcpy:

memcpy(inCompleteAQBuffer->mAudioData, THIS->mMyAudioBuffer + (THIS->mMyPlayBufferPosition / sizeof(float)), numBytesToCopy);

You'll also need to set the buffer size:

        inCompleteAQBuffer->mAudioDataByteSize =  numBytesToCopy;   

numBytesToCopy is always going to be the same, unless you're just about to run out of data. For example if your buffer is 2 seconds worth of audio data and you have 9 seconds to playback, then for the first four callbacks you will pass 2 second's worth. For the final callback you will only have 1 second's worth of data left. numBytesToCopy must reflect that.

    // Calculate how many bytes are remaining? It could be less than a normal buffer
    // size. For example, if the buffer size is 0.5 seconds and recording stopped
    // halfway through that. In which case, we copy across only the recorded bytes
    // and we don't enqueue any more buffers.
    SInt64 numRemainingBytes = THIS->mPlayBufferEndPosition - THIS->mPlayBufferPosition;

    SInt64 numBytesToCopy =  numRemainingBytes < THIS->mBufferByteSize ? numRemainingBytes : THIS->mBufferByteSize;

Finally, we advance the playback head. In our callback, we've given the queue some data to play. What happens next time we get the callback? We don't want to give the same data again. Not unless you're doing some funky dj loop stuff!

So we advance the head, which is basically just a pointer to our audio buffer. The pointer moves through the buffer like the needle on the record:

    SELF->mPlayBufferPosition += numBytesToCopy;

That's it! There's some other logic but you can get that from studying the full callback method in SpeakHere.

A couple of points I must emphasis. First, don't just copy and paste my code above. Absolutely make sure you understand what you are doing. Undoubtably you'll hit problems and you'll need to understand what's happening.

Secondly, make sure the audio formats are the same, and even better that you understand the audio format. This is covered in the Audio Queue Services Programming Guide in Recording Audio. Look at Listing 2-8 Specifying an audio queue’s audio data format.

It's crucial to understand that you have the most primitive unit of data, either an integer or a float. Mono or stereo you have one or two channels in a frame. That defines how many integers or floats are in that frame. Then you have frames per packet (probably 1). Your sample rate determines how many of those packets you have per second.

It's all covered in the docs. Just make sure everything matches up or you will have some pretty strange sounds!

Good luck!

Property Declaration - ivar and getter values don't match

7 votes

I have a doubt regarding property redeclaration

Overview:

  • class "A" is the parent class with a readonly property int n1;
  • class "B" is the subclass, which redeclares the property as read write
  • using the setter of class "B" the property value is set as 20
  • when i print the value using the getter and the instance variable I seem to get different values

Points to note: - Memory management = ARC (Automatic Reference Counting)

Question:

  • When I print the values of self.n1 and _n1 why do I get different values ?
  • My expected behavior and actual behavior don't match why (Pls scroll down to see the actual vs expected) ?

Code: (in separate files)

A.h

#import<Foundation/Foundation.h>

@interface A : NSObject

@property (readonly) int n1;

- (void) display;

@end

A.m

#import "A.h"

@implementation A

@synthesize n1 = _n1;

- (void) display
{
    printf("_n1     = %i\n", _n1);                  //I expected _n1 and self.n1 to display the same value
    printf("self.n1 = %i\n\n", self.n1);            //but they seem to display different values
}

@end

B.h

#import"A.h"

@interface B : A

@property (readwrite) int n1;

@end

B.m

#import"B.h"

@implementation B

@synthesize n1 = _n1;

@end

test.m

#import"B.h"

int main()
{
    system("clear");

    B* b1 = [[B alloc] init];

    b1.n1 = 20;

    [b1 display];   //Doubt - my expected behavior is different from actual behavior


    return(0);
}

Expected Behavior:

_n1     = 20
self.n1 = 20

Actual Behavior:

_n1     = 0
self.n1 = 20

There are two ways of going about this. In neither case do you call @synthesize in the subclass. I'm surprised that compiles for you. I would expect an error like "Property 'n1' attempting to use ivar '_n1' declared in superclass 'A'". In any case it's definitely not something you can really do, which is why you're seeing strange behavior. (I remembered why you aren't seeing this error; it's because of the separate compile units. You're just winding up with different ivars.)

First, you need to understand @dyanmic. This is a way of telling the compiler "yes, I know you don't see an implementation for the required method here; I promise it'll be there at runtime." In the subclass, you will use @dynamic to let the compiler know that it's ok to inherit n1.

@implementation B
@dynamic n1;
@end

Now, you need to provide the setN1: method. IMO, subclasses shouldn't go messing with their superclass's ivars, so I approve of the fact that synthesized ivars are marked @private. In a second, I'll tell you how to undo that, but for now let's deal with my preferred solution:

  • Implement setN1: as a private method in A.
  • Expose it in B.

A.h

@interface A : NSObject
@property (readonly) int n1;
- (void) display;
@end

A.m

#import "A.h"

@interface A () // Private class extension, causes setN1: to be created but not exposed.
@property (readwrite) int n1;
@end
@implementation A

@synthesize n1 = _n1;

- (void) display {
   ...
}
@end

B.h

#import "A.h"    
@interface B : A
@property (readwrite) int n1; // Tell the world about setN1:
@end

B.m

#import "B.h"
@implementation B
@dynamic n1; // Yes compiler, setN1: exists. I promise.
@end

Now, some people think it's fine for subclasses to mess with their superclass's ivars. Those people are wrong (ok, IMHO...) but it is possible in ObjC. You just need to declare the ivar @protected. This is the default when you declare ivars directly in the @interface (one of many reasons you shouldn't do this anymore). It would look like this:

A.h

@interface A : NSObject {
  int _n1;
}
...

A.m -- remove the extra class extension that makes n1 writable in the superclass.

B.h -- no change

B.m

@implementation B
@dynamic n1;

- (void)setN1:(int)n1 {
  _n1 = n1;
}
@end

Int to Double casting issue

7 votes

I'm an Objective-C developer with little C/C++ experience (and zero training), and I encountered something strange today with hard coded numeric values.

I'm sure it's a simple/stupid question, but can someone please explain why this works:

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -1.0001

And this also works (note number of seconds has changed):

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -2.0001

But this is executed immediately:

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -0.0001

However, using 4.0 instead of 4 fixes it:

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 4.0 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -4.0001

Why do 1 and 2 properly cast to the relevant double value, but bigger numbers (I tested 3 and 4) appear to be represented as 0?

I'm compiling with Xcode 4.2, configured to use LLVM 3.0.

EDIT:

dispatch_time_t is defined as:

typedef uint64_t dispatch_time_t;

And dispatch_time is:

dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

And NSEC_PER_SEC is:

#define NSEC_PER_SEC    1000000000  /* nanoseconds per second */

There are 1,000,000,000 nanoseconds in a second, so I'm going to assume that NSEC_PER_SEC is defined as 1000000000.

  • 4 is of type int
  • 4.0 is of type double

Now assuming that an int contains 32 bits, the range of an int would be [-2,147,483,648 to 2,147,483,647]

4000000000 > 2147483647, therefore you'll cause the int to overflow, which is causing the value to be set to 0.

EDIT: I probably could've worded the above statement better. The overflow could cause the int (assuming it's 32 bits in size, as stated above) to equal the value -294967296, and dispatch_time would be treating any value <= 0 as 0 seconds. That's where the "0" above came from.

A double variable can hold larger values than an int, and is able to store an approximation of the value 4000000000.

OpenCV C++/Obj-C: Proper object detection

7 votes

As some kind of "holiday project" I'm playing around with OpenCV and want to detect and measure stuff.

Current workflow (early stage - detection):

  1. Convert to grayscale (cv::cvtColor)
  2. Apply Adaptive threshold (cv::adaptiveThreshold)
  3. Apply canny edge detection (cv::Canny)
  4. Finding contours (cv::findContours)

My outcome is kinda crappy and I'm not sure what's the right direction to go. I already got cvBlob working under my current setup (OSX 10.7.2, Xcode 4.2.1) is it a better way to go? If so, how can I implement it the right way?

Or do I need background subtraction first? I tried that but wasn't able to find contours afterwards

Here's my image: image to measure

And that's my output, when I draw my contours back into the first image: output

UPDATE

I got it working in my programm and my output looks a bit different …

- (IBAction)processImage:(id)sender
{
    cv::Mat forground = [[_inputView image] CVMat];
    cv::Mat result = [self isolateBackground:forground];
    [_outputView setImage:[NSImage imageWithCVMat:result]];
}

- (cv::Mat)isolateBackground:(cv::Mat &)_image
{
    int rh = 255, rl = 100, gh = 255, gl = 0, bh = 70, bl = 0;
    cv::cvtColor(_image, _image, CV_RGB2HSV_FULL);
    cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
    cv::Mat bgIsolation;
    cv::inRange(_image, cv::Scalar(bl, gl, rl), cv::Scalar(bh, gh, rh), bgIsolation);
    bitwise_not(bgIsolation, bgIsolation);
    erode(bgIsolation, bgIsolation, cv::Mat());
    dilate(bgIsolation, bgIsolation, element);
    return bgIsolation;
}

This might be kind of a hack, but since it's a "holiday project", I'll post it anyway :)

Have you tried isolating the background and then inverting the mask (this would assume anything not background is an object, but it might work for what you want).

Below is the result I got using the OpenCV inRange function: enter image description here

You might want to smooth the image (pre-process) with GuassianBlur to get rid of some of the jagginess. I used a bigger dilation kernel than erosion kernel (5x5 vs. 3x3) to get rid of some noisy pixels. The smoothing might help this also tweaking the thresholds could make the erosion unnecessary. But, that should get you started.

Finally, here is my quick little code snippet I used to find this range:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
    Mat src = imread("test.jpg");


    int rh = 255, rl = 100, gh = 255, gl = 0, bh = 70, bl = 0;

    string windowName = "background";
    namedWindow(windowName);

    createTrackbar("rh", windowName, &rh, 255);
    createTrackbar("rl", windowName, &rl, 255);
    createTrackbar("gh", windowName, &gh, 255);
    createTrackbar("gl", windowName, &gl, 255);
    createTrackbar("bh", windowName, &bh, 255);
    createTrackbar("bl", windowName, &bl, 255);

    // for dilation
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));

    Mat bgIsolation;
    int key = 0;
    do
    {
        inRange(src, Scalar(bl, gl, rl), Scalar(bh, gh, rh), bgIsolation);

        bitwise_not(bgIsolation, bgIsolation);

        erode(bgIsolation, bgIsolation, Mat());
        dilate(bgIsolation, bgIsolation, element);

        imshow(windowName, bgIsolation);
        key = waitKey(33);
    } while((char)key != 27);

    waitKey();

    return 0;
}

Enjoy the holiday project! Looks fun :)

iOS Localizable.strings file for XIB files?

6 votes

I'm translating an iOS project into Portuguese and I've created a pt.lproj/Localizable.strings file, and I've added NSLocalizedString() into the source code. It all works! With one slight problem - the strings within XIB files don't get translated... I know this is by design though.

I've seen that the ibtool command can be used to rip strings from an XIB file called x and put it into a file called x.strings... but my question is, is there a way to pull the strings from ALL the xib files and put them all into one .strings file (e.g. Localizable.strings? or even another one called XIBs.strings would be fine?)

Thanks in advance guys :D!

You have two options how to translate xib files. One is you connect the UI elements to outlets and set your strings in your viewDidLoad method using the NSLocalizedString macros.

The second option is to provide a separate xib for each language your app supports. You don't have to create them manually, you can use the ibtool command (i assume your source language is English and target is Portugese):

ibtool --strings-file pt.lproj/Example.strings en.lproj/Example.xib –write pt.lproj/Example.xib

To collect strings found in your project you can use genstrings command - however i recommend using this python script to collect all your strings - it can nicely handle the situation when you need to add/remove strings to your app without having to translate and/or manually merge all previous strings

Edit

Oh and i found the article that i learned this trick from

Using Grand Central Dispatch outside of an application or runloop

6 votes

In the GCD documentation it's quite clear that to submit work to the main queue, you need to either be working within an NSApplication (or UIApplication) or call dispatch_main() to act as a run loop of sorts. However, do I need to do anything to setup the global concurrent queue?

Basically what I am asking is: If I write a plain simple C program, do I need to perform any special setup before I go dispatch_get_global_queue() and start giving it work?

No, you don't need any additional setup. But you need to call dispatch_main() to start the GCD dispatcher.
As dispatch_main() never returns, this will also prevent your main function from reaching it's return.

Example for a minimal C program that uses GCD & a global queue (based on http://wiki.freebsd.org/GCD):

#include <dispatch/dispatch.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, 5LL * NSEC_PER_SEC);
    dispatch_after(dispatchTime, globalQueue, ^{
        printf("Dispatched on global queue\n");
        exit(0);
    });
    dispatch_main();
    return (0);
}

To compile this, use:

clang -Wall -Werror -fblocks -L/usr/local/lib -I/usr/local/include -o test test.c

rss read iphone/ipad app

6 votes

I have an error while reading XML files for my iPhone app. I have a new feature on my iPhone app that reads my RSS feed. Everything looks good by but I have this issue:

Error while loading rss. Please check your Internet connection

Here's my code:

- (BOOL) readRSS {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [[NSURLCache sharedURLCache] setMemoryCapacity:0];
    [[NSURLCache sharedURLCache] setDiskCapacity:0];
    BOOL success = NO;
    NSXMLParser *parser = nil;
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://rss.domain.com/%@.xml", self.currentPage]];
    parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
    [parser setDelegate:self];
    [parser setShouldProcessNamespaces:NO];
    [parser setShouldReportNamespacePrefixes:NO];
    [parser setShouldResolveExternalEntities:NO];
    success = [parser parse];
    [parser release];
    [pool drain];
    return success;
}

Then I have this code:

- (void) cleartbl:(NSInteger)type {
    [[[self rssParser] rssItems] removeAllObjects];
    [_tableView reloadData];
    if(type == 1) {
        UIAlertView *alert = [[UIAlertView alloc] 
                          initWithTitle:@"RSS Feed" 
                          message:@"Error while loading rss. Please check your Internet connection."
                          delegate:nil 
                          cancelButtonTitle:@"OK" 
                          otherButtonTitles: nil];
        [alert show];   
        [alert release];
    }

Then i assign:

if([elementName isEqualToString:@"title"]){
    self.currentItem.title = self.currentItemValue;
}

What is my issue, am I missing something?

The code provided looks good for me, what I would do first is to check if your RSS is valid. I think you have an RSS issue here. You can use the RSS Validation to make sure everything looks good.

I would recommend to sanitize your RSS, keep it very simple, if you only want to display news or articles use letters and numbers in your text and use SEO friendly URLs.

This will simplify the data you are loading from your app and avoid errors like special characters.

Try with a simple RSS with one entry to start and you will see if your code has errors.

In Objective-C why is id used as return type for init methods?

6 votes

I did some quick searching and couldn't find an answer for this.

I'm interested to know why in Objective-C, id is used as the return type for init methods.

My guess is that it's because if the class is overridden, you don't want to return an object of the superclass's type, but I'm interested to know if it's done for some other reason.

Yup. Your idea is right on the money. A subclass should still be able to use its superclass's initialization methods and return its own type instead of the super type and returning id allows it to do that.