在块中使用 self 崩溃而不会出错



我有一个名为ServiceBrowser的类。在此类中,我有一个基于块的方法,用于搜索NSNetServices。

我这样称呼该方法:

[_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);
                    }];

如果我删除对fadeView:toAlpha:duration:completion:的调用,它会找到服务并将其注销。只有当我在这个块中使用self时,Xcode才会崩溃,没有任何错误记录到控制台。

fadeView:toAlpha:duration:completion:是 UIView 上的一个类别方法,它采用视图并将其淡入或淡出,这作为独立方法工作正常。问题是当我在块中使用_button时,它会崩溃。

我已经对此进行了调查,我认为它归结为保留周期。通过查看其他问题和博客文章,我应该在块内使用弱自我。

我尝试使用__block id weakSelf = self;,也typeof(self) __weak w_self = self;,但都不起作用。

您可以

尝试修改类,以便将self作为方法参数传递,并且类将引用传递回块:

[_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

类内部的实现将是:

- (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也可能是对的。请检查该对象是否仍然存在。

您可以尝试以下代码,该代码捕获块中self的弱引用(Fooself类)。

该块现在还注意,UIKit 方法将在主线程上执行:

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

如果在块执行时self被释放,将nil强引用strongSelf

编辑

为什么从 UI 元素捕获弱指针比捕获强指针更可取?虽然这可能不是崩溃的原因,但这是一个实质性的改进。

下面的块强烈地捕捉了self来证明这一点。请注意,当您直接引用 ivar 时,例如 _button而不是通过属性self.buttonself将被隐式捕获在块中,也就是说,它将保留到块完成之后。

现在,块的效果是,UI 元素self将保持活动状态,直到块执行之后,无论何时发生这种情况,也无论视图是否可见:用户可能在此期间切换到许多其他视图和控制器。但是,self在块完成之前不会被解除分配。这不必要的将资源保存在内存中(可能是大图像),到目前为止这些资源尚未释放。

[_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);
                    }];

相关内容

最新更新