为什么使用 "selector" 会导致编译器警告,而@selector(选择器)不会?



我搞砸了,只是尝试实现一个简单的非块/委托回调函数。

Class A.m
@implementation noblocks
-(void)logSomethingAndNotify:(id)object andCallSelector:(SEL)selector {
    //some task
    NSLog(@"TRYING THIS OUT");
    //implement callback functionality
    if ([object respondsToSelector:@selector(selector)]) {
        [object performSelector:@selector(selector) withObject:object];
    }
}
@end
Class B.m
- (void)viewDidLoad {
    [super viewDidLoad];
    ClassA *noblock = [noblocks new];
    [ClassA logSomethingAndNotify:self andCallSelector:@selector(addSubviewAfterDelay)];
}
-(void)addSubviewAfterDelay {
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        UIView *view = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
        view.backgroundColor = [UIColor blueColor];
        [self.view addSubview:view];
    });
}

在 A 类的实现文件中,如果我更改此语句:

if ([object respondsToSelector:@selector(selector)]) {            
[object performSelector:@selector(selector) withObject:object];
   }

选择器表示为方法参数的方式

if ([object respondsToSelector:selector]) {
[object performSelector:selector withObject:object];
}

然后我从编译器收到内存泄漏警告。

我知道,鉴于 Objective-C 的动态运行时,向未知对象发送无选择器消息可能会有问题 - 我们不知道该方法的返回类型,也无法确定是否应该保留返回的对象(如果有的话)。我不明白的是为什么使用 performSelector:@selector(selector) 与仅使用 performSelector:selector 不会引起任何 ARC 警告。

这个问题不是其他人解决编译器警告的重复 - 我的问题不是关于为什么显示错误,而是为什么一种方式显示警告而另一种方式没有。

@selector(selector)没有做你认为的那样。 :)

考虑:

    SEL selector = @selector(hash);
    NSLog(@"%s %s", selector, @selector(selector));

此输出(依赖于 SEL 实际上是一个 char* 作为实现细节的事实,您永远不应该依赖这样的实验之外):

   asdfasdfa[71281:7385265] hash selector

@selector(selector)生成一个常量值,该值不会触发 ARC 内存警告,因为编译器可以适当地推理代码路径。

即考虑:

    SEL bob = @selector(dobbs);
    NSLog(@"%s %s", bob, @selector(bob));

生产:

    asdfasdfa[71313:7394673] hobbs bob

变量bob是指SEL dobbs,而@selector(bob)产生SEL bob

在动态选择器值上使用performSelector:时出现警告的原因是,在 Cocoa 内存管理中,某些方法旨在返回保留的 (+1) 实例(即调用方有责任释放它)。默认情况下,这是名称以 allocretainnewcopymutableCopy 开头的方法。所有其他方法返回非保留实例(或保留并自动释放;不需要调用方释放的任何实例)。

performSelector: ,顾名思义,并不意味着返回保留的实例,也没有用 ns_returns_retained 进行注释。因此,ARC 会认为它不返回保留的实例(如果您使用它来执行"正常"方法,这是正确的)。但是,由于我们在编译时不知道选择器是什么,因此可能是您正在传递返回保留实例的选择器之一,其中它将是错误的并导致泄漏。

如果选择器在编译时是硬编码的,编译器可以在编译时检查名称,并验证它不是返回保留实例的名称之一。

相关内容

最新更新