Hobbes the Tige
Hobbes the Tige

Reputation: 3821

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];
}];

Upvotes: 8

Views: 7167

Answers (2)

Felix
Felix

Reputation: 35384

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.

Upvotes: 8

Christopher Pickslay
Christopher Pickslay

Reputation: 17762

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.

Upvotes: 3

Related Questions