Top
About

A personal blog on software development

Subscribe
Social Network Links
Search
Posts by category
Wednesday
17Mar2010

Multiple Xcode targets and info.plist

Another Xcode tip that I use a lot prompted by a question on StackOverflow.

When I have an Xcode iPhone OS project that requires multiple targets I like to create a directory for each target under the resources group. I can then place any target specific assets such as icons, data and the Info.plist file in one place. This helps a lot when it comes to selecting which items are included in which target build.

Note that the location of the Info.plist file needs to be set for each target in the build settings for the target (right-click on the target, get info and then look in the packaging section of the build tab).

A common and easy mistake to make when selecting which files are included with each target is to select the Info.plist file. This causes a build warning as follows:

Warning: The Copy Bundle Resources build phase contains this target’s Info.plist file ‘Resources/AppTarget1/Info.plist’

If you are building for a device the codesign operation will also fail:

Command /usr/bin/codesign failed with exit code 1

The solution is easy enough, just find the offending Info.plist file and uncheck its target setting. Apple has some more details on the problem in Technical Q&A QA1649.

Tuesday
16Mar2010

Downloading iPhone app data with the organizer

Filed under the category of how did I not know about this before now…

Sometimes when you are debugging an iPhone app it is useful to be able to take a peek at the files actually created and stored on the device. Unfortunately there is no way to mount the sandboxed application filesystem from your Mac (at least not without jailbreaking).

Whilst playing around with some provisioning profiles yesterday I did stumble on way to do this from the Xcode Organizer window. I am sure this is documented somewhere by Apple and that I just missed it or forgot about it but just in case somebody else has also missed this:

With your device attached, find your app in the application window and expand the entry. Below the application name it shows an icon for the Application Data and on the right hand side is another small icon to download the data from the device. Since a picture is worth a thousand words:

The downloaded data includes the applications Documents, Library and tmp directories. Of course it only works for applications that you have deployed to the device.

Monday
15Mar2010

Code signing error CSSMERR_DL_MISSING_VALUE

Just been hit by this mysterious Xcode code-signing error when building an iPhone OS app for the device for the second time in a few weeks. The annoying thing is I forgot what caused it the first time so it wasted a few minutes again whilst I remembered the solution. So here for posterity is the cause.

I have a USB security key for use when logging in to Virtual Private Networks. The key serves my security certificate to the login process which is very convenient. However when I try to code sign with Xcode it seems that instead of going to the OSX keychain it tries to find my iPhone developer certificate on the USB key which is never going to work.

The simple solution is to remember to take the USB key out when I am finished with it. Now if I can only remember that the next time it happens…

Monday
15Mar2010

NSFetchedResultsController and sort performance

I really liked the NSFetchedResultsController when it was introduced in iPhone OS 3.0. It has made it much easier to implement a core data backed table view and removes the need to write a lot of code. It also seems to do a pretty good job of keeping the memory footprint to a minimum.

However, one thing I have been struggling with is with performance when using a grouped table. To be more precise the issue is not really with the fetched results controller but with the sort descriptor when using a case insensitive search. The basic setup is as follows:

// Create a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Record"
inManagedObjectContext:moc];
[fetchRequest setEntity:entity];

// Create a sort descriptor for the request
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"value"
ascending:YES
selector:@selector(localizedCaseInsensitiveCompare:)];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

// Now create the fetched results controller
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:sharedMoc
sectionNameKeyPath:@"valueForSectionTitle"
cacheName:@"cache"];

[fetchRequest release];
[sortDescriptor release];

The model named “Record” has a string attribute named “value” that is used to order the contents of the table. A method defined for the Record model “valueForSectionTitle” returns the value used for generating the section title for each record. It looks something like this (assuming UTF8 strings):

- (NSString *)valueForSectionTitle {
// Return the first character of the value
// converted to uppercase
return [[self.value substringToIndex:1] uppercaseString];
}

This approach works but even with small datasets of around 1,000 records there is a noticeable lag in the user interface when the table view loads. Turning on core data SQL debugging shows what is happening:

2010-03-15 14:12:28.633 CorePerf[2367:207] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT,
t0.ZVALUE FROM ZRECORD t0 ORDER BY t0.ZVALUE COLLATE NSCollateLocaleSensitiveNoCase
2010-03-15 14:12:29.654 CorePerf[2367:207] CoreData: annotation: sql connection fetch
time: 0.8731s
2010-03-15 14:12:29.662 CorePerf[2367:207] CoreData: annotation: total fetch execution
time: 1.0311s for 1500 rows.

So for 1500 records it takes over a second for the fetch request to execute. To avoid this runtime delay it make sense to compute the section index title (in this the uppercase initial letter of the string) ahead of time and avoid the case insensitive search. Adding a new attribute to the core data model to hold this initial letter simplifies the sort descriptor removing the need for a sort comparison selector.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"indexValue"
ascending:YES];

The impact on the fetch request is dramatic:

2010-03-15 14:21:25.580 CorePerf[2397:207] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT,
t0.ZVALUE, t0.ZINDEXVALUE FROM ZRECORD t0 ORDER BY t0.ZINDEXVALUE
2010-03-15 14:21:25.925 CorePerf[2397:207] CoreData: annotation: sql connection fetch
time: 0.2101s
2010-03-15 14:21:25.934 CorePerf[2397:207] CoreData: annotation: total fetch execution
time: 0.3537s for 1327 rows.

The fetch time drops from 0.8731s to 0.2102s which is a 75% improvement. When I get some more time I will experiment with some different data set sizes but for now this seems to suggest a definite conclusion:

Arrange your data so that you can use the default sort comparison selector when creating core data fetch requests.

Friday
12Mar2010

Finding memory leaks in Xcode

I have found over time I have collected a set of environment variables and arguments that I end up leaving in the Arguments window of an executable so that I can quickly turn them on and off when I need them.

Arguments to be passed on launch:

  • -com.apple.CoreData.SQLDebug 1

Variables to be set in the environment (The values for all variables should be set to YES):

  • NSZombieEnabled
  • NSDebugEnabled
  • MallocStackLogging
  • MallocStackLoggingNoCompact

I should be clear that whilst I leave these parameters permanently in the Arguments settings I do NOT enable them by default (ensure the checkbox is not ticked). That would not be a good idea and you definitely do not want to ship an application with any of these arguments set.

I talked about the core data SQLDebug argument in a previous post. From the others the one that is most useful is NSZombieEnabled which catches objects as they are being deallocated and prevents them from being freed (though you can change that). The benefit is that if an application attempts to access a deallocated object it gets logged to the console which makes it easy to track down the problem.

2010-03-12 18:05:17.668 UICatalog[4170:207] *** -[UIDeviceRGBColor setFill]: message sent to deallocated instance 0x190d110

The other arguments are useful when used with Guard Malloc to track down applications that are corrupting the heap or scribbling over freed memory.

Of course the easiest way to find a memory leak these days is to use the leaks instrument but I still find it useful to have a command-line/console solution in some situations such as when running a set of unit tests.