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
/
#