views:

298

answers:

4

It is really mind boggling to find out many differences between the iphone and the simulators. I spent several hours trying to figure out why my application ran on the simulator but crashed on my iphone device. It turns out the culprit is sortedArrayUsingDescriptors. Are there more get-you like this? Please share with me.

To share with you on the issue and the fixes:


Code crashed on iphone but not simulator

NSSortDescriptor* aDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"count" ascending:NO] autorelease];
NSArray* anNsArray = [[NSArray alloc] init];
NSArray* aSortedNsArray = [[NSArray alloc] init];

aSortedNsArray = [anNsArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:aDescriptor]];

The issue is on [NSArray arrayWithObject:aDescriptor];


The fixes are to create an Array to store it:

NSArray* descriptorArray = [[NSArray alloc] initWithObjects:countDescrp, nil];
aSortedNsArray = [anNsArray sortedArrayUsingDescriptors:descriptorArray];

Wayne in Campbell, CA

+3  A: 

The posted code does not crash. While there are a couple of leaked objects, it does not crash on the simulator, nor on the device.

I think, your problem is elsewhere. Try narrowing it down using a new project and copy only suspicious code.

Nikolai Ruhe
+2  A: 

Nikolai,

It did not crashed when my application was simple and small. It could be to do with autorelease and release as stated in this post: http://kosmaczewski.net/2009/01/28/10-iphone-memory-management-tips/ where the author points out another but similar issue:

"I’m sure you’ve experienced your application crashing when using NSDictionary’s dictionaryWithObjects:forKeys: and then finding out that a replacing that with initWithObjects:forKeys: made your application run just fine."

Using [NSArray arrayWithObject:aDescriptor], the NSArray is created with autorelease; in contrast, using [[NSArray alloc] initWithObjects:countDescrp, nil] requires specifically when to release the NSArray.

The simple change in code made my application not crashing 100% of the time on the iphone while the old code made the application crashed 100% of the time.

Wayne Lo
Again: The code you posted is not responsible for the crash you experienced. In your question, you stated that `sortedArrayUsingDescriptors` does not work as expected on the device, which is wrong. Your bug is probably due to bad memory management. Just poking around, switching `arrayWithObject:` for `initWithObject:` without understanding what’s going on is not really helpful. My advice is: Read and understand the Cocoa memory management guide: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Nikolai Ruhe
A: 

Nikolai,

You could be right. Most coders have a tough time to rule out the possibility of bad memory management using Objective C. If I ever find out the other true underlying bug as you have speculated, I will update here. In the meant time, I would remind coders to be aware of the differences between arraywithobjects and initwithobjejcts; use them wisely. Thanks for responding.

Wayne

Wayne Lo
The differences between `arrayWithObject:` and `initWithObject:` are obvious and well known. That’s why I pointed you to the Cocoa memory management guide: It’s all in there and should be considered fundamental to every Cocoa programmer.
Nikolai Ruhe
+1  A: 
NSSortDescriptor* aDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"count" ascending:NO] autorelease]; 
NSArray* anNsArray = [[NSArray alloc] init];
NSArray* aSortedNsArray = [[NSArray alloc] init];

aSortedNsArray = [anNsArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:aDescriptor]];

This is a wrong initialization mechanism, and if the code snippet is complete, your problem lies on the anNsArray object that's empty.

You also do not need to initialize the aSortedNsArray.

So it should be:

NSSortDescriptor* sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"count" ascending:NO] autorelease]; 

// Assume you return it as array of objects from a property or method
NSArray *array = [self objects]; 
NSArray *sortedArray = nil;
if ([array count] > 0) {
     sortedArray = [array sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
}

// Then you check the sortedArray
if (sortedArray == nil || [sortedArray count] == 0)
   [self somethingIsWrong];     

arrayWithObject: (autoreleased) or initWithObject: (manual) is just a different way to allocate NSArray object. It won't cause crashes normally. Because what you care about is the sortedArray not retaining the descriptors array object.

Jesse Armand