1

I have a Class called ServiceBrowser. Inside this class I have a block based method that searches for NSNetServices.

I call the method as such :

[_serviceBrowser discoverServicesOfType:@"_theService._tcp."
                               inDomain:@"local."
                    didDiscoverServices:^(NSArray *services) {

                        NSLog(@"Services discovered %@", services);

                        [UIView fadeView:_button toAlpha:1.0 duration:0.5 completion:nil];

                    } didRemoveServices:^(NSArray *services) {

                        NSLog(@"Services removed %@", services);

                    } failure:^(NSString *message) {

                        NSLog(@"Failure %@", message);

                    }];

If I remove the call to fadeView:toAlpha:duration:completion: it finds the services and logs them out. Only when I use self inside this block Xcode crashes without any error logged to the console.

fadeView:toAlpha:duration:completion: is a category method on UIView that takes a view and fades it in or out and this works fine as a standalone method. The problem is when I use _button inside the block it crashes.

I have investigated this and I assume it is down to a retain cycle. From looking at other questions and blog posts I should use a weak self inside the block.

I have tried using __block id weakSelf = self; and also typeof(self) __weak w_self = self; and neither work.

Zack Brown
  • 5,990
  • 2
  • 41
  • 54
Ste Prescott
  • 1,789
  • 2
  • 22
  • 43

2 Answers2

0

You could try to modify your class so that you pass self as method parameter and the class passes the reference back to the block:

[_serviceBrowser discoverServicesOfType:@"_theService._tcp." inDomain:@"local." didDiscoverServices:^(NSArray *services, id sender) {
//                                                                                                                       ^^^^^^^^^ "sender" is "self"

                } didRemoveServices:^(NSArray *services) {

                } failure:^(NSString *message) {

                } fromSender:self];
//                ^^^^^^^^^^^^^^^ pass self here

The implementation inside the class would be kind of:

- (void)discoverServicesOfType:(NSString *)type inDomain:(NSString *)domain didDiscoverServices:^(NSArray *, id)discoveredBlock fromSender:(id)sender {
    NSMutableArray *services = [NSMutableArray array];
    // do some fancy stuff here
    discoveredBlock(services, sender);
}

Anc Ainu could also be right. Please check whether or not the object still exists.

Julian F. Weinert
  • 7,474
  • 7
  • 59
  • 107
  • Feels a bit hacky to me too. But actually it's not. You'r just passing around pointers... Nothing wrong about it. Please consider accepting, if it works :) – Julian F. Weinert Oct 25 '13 at 09:49
0

You might try the following code which captures a weak reference of self within the block (Foo is the class of self).

The block also takes care now, that UIKit methods will be executed on the main thread:

__weak Foo* weakSelf = self;
[_serviceBrowser discoverServicesOfType:@"_theService._tcp."
                               inDomain:@"local."
                    didDiscoverServices:^(NSArray *services) {
                        NSLog(@"Services discovered %@", services);
                        Foo* strongSelf = weakSelf;
                        if (strongSelf) {
                            dispatch_async(dispatch_get_main_queue(), ^{
                                [UIView fadeView:strongSelf.button 
                                         toAlpha:1.0 
                                        duration:0.5 completion:nil];
                            });
                        }
                    } didRemoveServices:^(NSArray *services) {
                         NSLog(@"Services removed %@", services);
                    } failure:^(NSString *message) {
                         NSLog(@"Failure %@", message);
                    }];

Should self be deallocated when the block executes, the strong reference strongSelf will be nil.

Edit

Why is capturing a weak pointer from a UI element preferred over capturing a strong pointer? While this is likely not the cause of your crash, it is a substantial improvement.

The block below strongly captures self to demonstrate this. Note, when you reference an ivar directly e.g. _button and not via the property self.button, self will be implicitly captured in the block, that is it will be retained until after the block has finished.

Now, the effect of the block is, that the UI element self will be held alive until after the block executes, no matter when this happens and no matter if the view is visible anymore: the user might have switched to numerous other views and controllers in the meantime. However, self don't get deallocated until after the block finishes. This unnecessary holds resources in memory (possibly large images) which don't get freed up to this point.

[_serviceBrowser discoverServicesOfType:@"_theService._tcp."
                               inDomain:@"local."
                    didDiscoverServices:^(NSArray *services) {
                        NSLog(@"Services discovered %@", services);
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [UIView fadeView:self.button 
                                     toAlpha:1.0 
                                    duration:0.5 completion:nil];
                        });
                    } didRemoveServices:^(NSArray *services) {
                         NSLog(@"Services removed %@", services);
                    } failure:^(NSString *message) {
                         NSLog(@"Failure %@", message);
                    }];
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • Is _strongSelf_ `nil` or does it point to the original `self` when it crashes? Well, there are only two possibilities: either `self` got deallocated when the block executes: then _strongSelf_ will be `nil` and your block does effectively nothing and should not crash. Otherwise, _strongSelf_ is not `nil` and thus pointing to a valid `Foo`. Then, why should it crash? This indicates your issue is elsewhere. You might also "retain" the object `Foo` until the block finishes, through defining a strong pointer outside, eg. `Foo* strongSelf = self;` and then use `strongSelf` within the block. – CouchDeveloper Oct 24 '13 at 21:19
  • Why would this be relevant? The block currently has a strong reference to `self`. So it does not need to worry about `self` being deallocated. – newacct Oct 25 '13 at 08:55
  • @newacct You are correct, that `_button` implicitly captures `self`. However, when the view will disappear before the block executes, it makes no sense to execute animations. Thus, the _weakSelf_ pointer should avoid this. If the view is still visible, my code above is effectively the same. When it crashes anyway, then the problem is elsewhere - probably executing the block not on the main thread. – CouchDeveloper Oct 25 '13 at 09:48
  • @StePrescott I made a modification to the code. If the block does not execute on the main thread, the original code might crash since it calls UIKit methods. Now, it dispatches the UIKit methods on the main thread. – CouchDeveloper Oct 25 '13 at 09:54