Showing posts with label optimization. Show all posts
Showing posts with label optimization. Show all posts

Monday, June 24, 2013

Hidden Gems in Cocoa Touch / XCode

1. Open Quickly (Command-Shift-O)
2. View Related Files From Toolbar
3. Use breakpoint actions such as log message or play a sound to avoid recompiling.
4. Use debugDescription.
5. Use Objective-C Subscripting.
NSMutableArray *indexedValues = [NSMutableArray array];
indexedValues[0] = @"One";
NSLog(@"value: %@", indexedValues[0]);
NSMutableDictionary *keyedValues = [NSMutableDictionary dictionary];
keyedValues[@"color"] = [UIColor blueColor];
NSLog(@"value: %@", keyedValues[@"color"]);
6. Private declarations not necessary since Xcode 4.3
7. Synthesize not necessary for @property since Xcode 4.4
8. Reverse arrays quickly inline

 NSArray *numbers = @[ @1, @2, @3 ];
 NSArray *reversed = numbers.reverseObjectEnumerator.allObjects;
9.  Guarantee a mutable object

 NSArray *unknown = self.values; // may be nil
 NSMutableArray *newArray = [NSMutableArray arrayWithArray:unknown];
10. Declare and enumerate different collection types

 id collection = values;
 for (id object in collection) {
... }
11. Remove duplicate values in array without NSSet

[array valueForKeyPath:@"@distinctUnionOfObjects.self"]
12. CAGradientLayer

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(150, 250, 500, 500);
UIColor *c1 = [UIColor colorWithRed:0.09 green:0.70 blue:0.98 alpha:1.0];
UIColor *c2 = [UIColor colorWithRed:0.07 green:0.41 blue:0.95 alpha:1.0];
UIColor *c3 = [UIColor colorWithRed:0.81 green:0.46 blue:0.93 alpha:1.0];
gradient.colors = @[(id)c2.CGColor, (id)c3.CGColor, (id)c3.CGColor];
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"colors"];
anim.toValue = @[(id)c1.CGColor, (id)c2.CGColor, (id)c2.CGColor];
anim.duration = 4.0;
anim.autoreverses = YES;
anim.repeatCount = 1e100;
[gradient addAnimation:anim forKey:@"colors"];
[self.view.layer addSublayer:gradient];

13. Core Data Private Queue

NSManagedObjectContext *bgContext;
bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:
                                    NSPrivateQueueConcurrencyType];
[context performBlock:^{
    // (add, remove, change objects.)
    saveCompleted = [context save:& saveError];
}];

14. Core Data
Really fast fetches
• Only specific properties

 NSFetchRequest
 fetch.propertiesToFetch = @[@"name", @"phone"];
• Only the raw values

fetch.resultType = NSDictionaryResultType 
• Only the object id

 fetch.resultType = NSManagedObjectIDResultType
• Only the count

 fetch.resultType = NSCountResultType

WWDC 2013 Core Data Performance - Optimization and Debugging

These are the things that I thought were most useful to me:

1. Don't fetch more than you need. Only 10 or so rows are visible so set a batch size on your NSFetchRequests.

Example:


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Contact"];

request.fetchBatchSize = 20;


2. Optimize your data model. Put binary data in separate entities and use external storage. Duplication isn't always a bad thing, if it speeds up your data query.

3. Prefetch relationships if you know you need them.

4. Consider returning dictionaries instead of managed objects.

Example:


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Quake"];

[request setResultType:NSDictionaryResultType];

[request setPropertiesToFetch:@[@"magnitude"]];


5. Use SQLite to perform your calculations.

Example:


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Quake"];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
 ed.name = @"minimum";
 ed.expression = [NSExpression expressionForFunction:@"min:"
            arguments:@[ [NSExpression expressionForKeyPath:@"magnitude"]]];
[request setPropertiesToFetch:@[ ed ]];


6. Use SQLite to group your results automatically.

Example:


NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
 ed.name = @"count";
 ed.expression = [NSExpression expressionForFunction:@"count:"
arguments:@[ [NSExpression expressionForKeyPath:@"magnitude"]]];
 [request setPropertiesToFetch:@[ @"magnitude", ed ]];
 [request setPropertiesToGroupBy:@[ @"magnitude" ]];
7. Use SQL Logging.

Pass argument flags = {1,2,3} on launch:


-com.apple.CoreData.SQLDebug 1


8. Optimize predicates. Text comparisons are expensive. Put numeric comparisons first.


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Contact"];
Bad:


request.predicate = [NSPredicate predicateWithFormat:@"firstName == %@ AND age > %i", @"John", 40];
Good:

request.predicate = [NSPredicate predicateWithFormat:@"age > %i && firstName == %@", 40, @"John"];
9. Predicate Costs (Low to High):


Beginswith/Endswith < Equality < Contains < Matches



iOS 5 NSFetchedResultsController Sorting Issues With RestKit

We were having issues with NSManagedObjects not being sorted correctly after modifying managed objects locally and saving, but we didn't know why so I had to do some research. I found this post: http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains which was helpful, and the code snippet below was what fixed it. The issue in iOS 5 is that the NSFetchedResultsController deadlocks when there are changes in a child context. So with RestKit on that particular view controller we just had to switch from using the mainQueueManagedObjectContext to mainQueueManagedObjectContext.parentContext for iOS 5 and it resolved all of our issues.
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)


  NSManagedObjectContext *newManagedObjectContext;
  if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
    newManagedObjectContext = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext;
  } else{
    newManagedObjectContext = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext.parentContext;
  }
  NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
  NSFetchedResultsController *newFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:newManagedObjectContext sectionNameKeyPath:nil cacheName:nil];