Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C accessing properties inside block

I have read Apple's Blocks Programming Topics and my due diligence searching online, but I am still unclear if I am implementing my blocks correctly. I have an array of clients as a property that is populated when an NSNotification is sent. Clients is used as a tableview data source. The code below works, but I am curious if it is putting self in a retaining cycle. Should I do something like __block id theClients = self.clients; and then reference theClients inside the block?

@property (strong, nonatomic) NSMutableArray *clients;

NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__block id observer = [notifyCenter addObserverForName:queryHash
                                                object:nil
                                                 queue:[[NSOperationQueue alloc] init]
                                            usingBlock:^(NSNotification* notification){
                                                // Explore notification

    if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
        NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
        if (self.clients)
        {
            self.clients = nil;
        }
        self.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
        for (NSDictionary *row in rows) {
            [self.clients addObject:row];
        }
    } else {
        NSLog(@"CLIENTS ERROR Returned: %@",[notification.userInfo objectForKey:kerrorReturnKey]);
    }

    [[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
like image 834
Hobbes the Tige Avatar asked Aug 21 '12 13:08

Hobbes the Tige


2 Answers

There is no problem in accessing the clients property because it is a strong (i.e. retained) property. So you don't need the __block here.

One problem can be that self might not exist anymore when the notification is sent. Then you would access the deallocated object and the app can crash! To avoid that you should remove the observer in the dealloc method.

The __block before id observer is definitely required !

EDIT:

In iOS 5 you can safely capture self using a weak reference:

__weak id weakSelf = self;

Then inside the block you can safely use weakSelf.clients. The variable weakSelf will turn into nil automatically when the object is deallocated.

like image 172
Felix Avatar answered Sep 23 '22 19:09

Felix


Yes, you have a retain cycle, at least until the notification occurs. When you access the clients ivar in the block, the block will retain self. It will be retained by the block in notification center until the notification occurs (since you remove the observer at the end of the block). If that's not desirable in your case, you can use a weak reference to self.

NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__weak id weakSelf = self;
id observer = [notifyCenter addObserverForName:queryHash
                                        object:nil
                                         queue:[[NSOperationQueue alloc] init]
                                    usingBlock:^(NSNotification* notification) {
    if (weakSelf) {                                
        if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
            NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
            if (weakSelf.clients)
            {
                weakSelf.clients = nil;
            }
            weakSelf.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
            for (NSDictionary *row in rows) {
                [weakSelf.clients addObject:row];
            }
        } else {
            NSLog(@"CLIENTS ERROR Returned: %@",[notification.userInfo objectForKey:kerrorReturnKey]);
        }
    }    
    [[NSNotificationCenter defaultCenter] removeObserver:observer];
}];

I don't see any reason you need to __block qualify observer.

It's also not clear you're getting anything out of using the block-based API here. If you don't want to worry about the potential retain cycle, you could just use addObserver:selector:name:object: and put the body of your notification callback in an instance method.

like image 32
Christopher Pickslay Avatar answered Sep 26 '22 19:09

Christopher Pickslay