Paul A. Jungwirth
Illuminated Computing
PDX iOS
March 2014



Options:

// branch: master
@implementation MyAppDelegate {
  NSArray *_restaurants;
}
- (BOOL) application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [self fetchRestaurants];
}
// . . .
@end
// branch: master
- (BOOL) application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [self fetchRestaurants];
  [NSTimer scheduledTimerWithTimeInterval:60*60
                                   target:self
                                 selector:@selector(fetchRestaurants)
                                 userInfo:nil
                                  repeats:YES];
  // . . .
}
// branch: sync
- (void) fetchRestaurants {
  NSURL *url = [NSURL URLWithString:kAPIRestaurantsURL];
  NSURLRequest *req = [NSURLRequest requestWithURL:url];
  NSURLResponse *resp;
  NSError *err;
  NSData *d = [NSURLConnection sendSynchronousRequest:req
                                  returningResponse:&resp
                                              error:&err];
  if (d) {
    self->_restaurants = [MyRestaurant parseJSON:d];
  }
}

// branch: async-notification
- (void) fetchRestaurants {
  NSURL *url = [NSURL URLWithString:kAPIRestaurantsURL];
  NSURLRequest *req = [NSURLRequest requestWithURL:url];
  NSOperationQueue *q = [NSOperationQueue mainQueue];
  [NSURLConnection sendAsynchronousRequest:req
                                       queue:q
                             completionHandler:
    ^(NSURLResponse *resp, NSData *d, NSError *err) {
      if (d) {
        self->_restaurants = [MyRestaurant parseJSON:d];
      }
    }];
}
// branch: async-notification
@implementation MyRestaurant {
+ (NSArray *) parseJSON:(NSData *)d {
  NSMutableArray *restaurants = [NSMutableArray new];
  NSError *jsonError = nil;
  NSArray *restFile =
    [NSJSONSerialization JSONObjectWithData:d
                                    options:0
                                      error:&jsonError];
  for (NSDictionary *dict in restFile) {
    MyRestaurant *r = [[MyRestaurant alloc] initWithDictionary:dict];
    [restaurants addObject:r];
  }
  return restaurants;
}
// branch: async-notification
@implementation MyParseRestaurantsOperation {
    NSData *_data;
}
- (id)initWithData:(NSData *)d {
    if (self = [super init]) {
        self->_data = d;
        return self;
    }
    return nil;
}
- (void)main {
    self.restaurants = [MyRestaurant parseJSON:self->_data];
    [[NSNotificationCenter defaultCenter]
      postNotificationName:@"ParseRestaurantsOperationFinished"
                                                   object:self];
}
@end
// MyAppDelegate.m:
// branch: async-notification
- (void)fetchRestaurants {
    NSURL *url = [NSURL URLWithString:kAPIRestaurantsURL];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    NSOperationQueue *q = [NSOperationQueue mainQueue];
    [NSURLConnection sendAsynchronousRequest:req
                                       queue:q
                           completionHandler:
     ^(NSURLResponse *resp, NSData *d, NSError *err) {
         if (d) {
             MyParseRestaurantsOperation *op =
               [[MyParseRestaurantsOperation alloc] initWithData:d];
             [[NSNotificationCenter defaultCenter] addObserver:self
                             selector:@selector(parsedRestaurants:)
                          name:@"ParseRestaurantsOperationFinished"
                                                         object:op];
             NSOperationQueue *background = [NSOperationQueue new];
             [background addOperation:op];
         }
     }];
}
// MyAppDelegate.m:
// branch: async-notification
- (void)parsedRestaurants:(NSNotification *)n {
    [self performSelectorOnMainThread:@selector(updateRestaurants:)
      withObject:((MyParseRestaurantsOperation*)[n object]).restaurants
                                                      waitUntilDone:NO];
}
- (void)updateRestaurants:(NSArray *)restaurants {
    self->_restaurants = restaurants;
    [((MyViewController *)self.window.rootViewController).tableView reloadData];
}
// MyAppDelegate.m:
// branch: async-completion-block
  MyParseRestaurantsOperation *op =
    [[MyParseRestaurantsOperation alloc] initWithData:d];
  [op setCompletionBlock:^{
    [self performSelectorOnMainThread:@selector(updateRestaurants:)
                                          withObject:op.restaurants
                                                  waitUntilDone:NO];
  }];
  NSOperationQueue *background = [NSOperationQueue new];
  [background addOperation:op];

https://developer.apple.com/videos/wwdc/2011/
If a block is copied, then it retains whatever it closed over.
If you are in turn retaining the block (perhaps indirectly), you have a retain cycle.
Usually it's a problem with self.
Watch out closing over _ivars! (self->_op)

// MyAppDelegate.m:
// branch: async-completion-block
  MyParseRestaurantsOperation *op =
    [[MyParseRestaurantsOperation alloc] initWithData:d];
  __weak MyParseRestaurantsOperation *weakOp = op;
  [d setCompletionBlock:^{
    MyParseRestaurantsOperation *strongOp = weakOp;
    if (!strongOp) return;
    [self performSelectorOnMainThread:@selector(updateRestaurants:)
                                    withObject:strongOp.restaurants
                                                  waitUntilDone:NO];
  }];
  NSOperationQueue *background = [NSOperationQueue new];
  [background addOperation:op];
// MyAppDelegate.m:
// branch: async-block-as-nsoperation
- (void)fetchRestaurants {
    NSURL *url = [NSURL URLWithString:kAPIRestaurantsURL];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    NSOperationQueue *q = [NSOperationQueue mainQueue];
    [NSURLConnection sendAsynchronousRequest:req
                                       queue:q
                           completionHandler:
     ^(NSURLResponse *resp, NSData *d, NSError *err) {
         if (d) {
             NSOperationQueue *background = [NSOperationQueue new];
             [background addOperationWithBlock:^{
                 NSArray *restaurants = [MyRestaurant parseJSON:d];
                 [self performSelectorOnMainThread:@selector(updateRestaurants:)
                                                          withObject:restaurants
                                                               waitUntilDone:NO];
             }];
         }
     }];
}
// MyAppDelegate.m:
// branch: async-urlconnection-callback-different-queue
- (void)fetchRestaurants {
    NSURL *url = [NSURL URLWithString:kAPIRestaurantsURL];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    NSOperationQueue *q = [NSOperationQueue new];
    [NSURLConnection sendAsynchronousRequest:req
                                       queue:q
                           completionHandler:
     ^(NSURLResponse *resp, NSData *d, NSError *err) {
         if (d) {
             NSArray *restaurants = [MyRestaurant parseJSON:d];
             [self performSelectorOnMainThread:@selector(updateRestaurants:)
                                                      withObject:restaurants
                                                           waitUntilDone:NO];
         }
     }];
}
Set up a dispatch_queue with GCD:
// branch: async-gcd
@implementation MyAppDelegate {
  NSArray *_restaurants;
  dispatch_queue_t _restaurants_queue;
}
- (BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self->_restaurants_queue = dispatch_queue_create("com.example.restaurants", NULL);
  [self fetchRestaurants];
}
Do our parsing on our own queue:
// branch: async-gcd
- (void)fetchRestaurants {
  NSURL *url = [NSURL URLWithString:kAPIRestaurantsURL];
  NSURLRequest *req = [NSURLRequest requestWithURL:url];
  NSOperationQueue *q = [NSOperationQueue mainQueue];
  [NSURLConnection sendAsynchronousRequest:req
                                     queue:q
                         completionHandler:
    ^(NSURLResponse *resp, NSData *d, NSError *err) {
      if (d) {
        dispatch_async(self->_restaurants_queue, ^{
          NSArray *restaurants = [MyRestaurant parseJSON:d];
          dispatch_async(dispatch_get_main_queue(), ^{
            self->_restaurants = restaurants;
            [((MyViewController *)self.window.rootViewController).tableView reloadData];
          });
        });
      }
    }];
}
Paul Jungwirth
/
#