zoukankan      html  css  js  c++  java
  • performSelector 方法的自己主动俘获特性

    局部变量自己主动俘获

    偶然在调试中发现,performSelector 方法具有自己主动俘获变量的特性。试看例如以下代码:

    CGFloat c = _addViewShowing ? 0 : 80;
        if([self respondsToSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:)]){
            [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil];
            ...
        }

    这里请注意 [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil]; 一句。

    在调用 performSelector 方法时,会自己主动把变量 CGFloat c 俘获到 jsq_setToolbarBottomLayoutGuideConstant: 方法调用中去。也就是说,相当于向该方法传递了參数 c。

    值得注意的是。变量 c 的类型必须和 jsq_setToolbarBottomLayoutGuideConstant: 方法參数的类型同样。否则不会自己主动俘获。比如, c 变量为 CGFloat,而方法jsq_setToolbarBottomLayoutGuideConstant: 的參数同样也为 CGFloat:

    - (void)jsq_setToolbarBottomLayoutGuideConstant:(CGFloat)constant

    假设你将 c 的类型改成 int。则 c 不会被自己主动俘获。

    利用这个特性,我们能够在 performSelector 调用时,自己主动传递变量给目标方法,而不用通过 withObject 来传參。

    自己主动俘获方法參数

    假设我们将上述代码定义为一个方法:

    -(void)setToolbarSpaceToBottom:(CGFloat)constant{
        // 调用私有方法 jsq_setToolbarBottomLayoutGuideConstant
        CGFloat c = _addViewShowing ? 0 : 80;
        if([self respondsToSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:)]){
            [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil];
        }
    }

    则变量 c 能够省略,由于 performSelector 会自己主动俘获方法參数 constant。将之传递给 jsq_setToolbarBottomLayoutGuideConstant: 调用。于是这种方法能够写成:

    -(void)setToolbarSpaceToBottom:(CGFloat)constant{
        if([self respondsToSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:)]){
            [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil];
        }
    }
    

    自己主动俘获的代价

    上述代码同一时候会带来一个负面作用。即在两个 @selector 引用的地方出现两个同样编译警告:

    Undeclared selector ‘jsq_setToolbarBottomLayoutGuideConstant:’

    这是由于 jsq_setToolbarBottomLayoutGuideConstant: 方法来自于父类。它是私有的(没有将方法进行静态声明——即未在头文件里声明)。

    对一切未静态声明的方法进行 performSelector 时。编译器都会提示 Undeclared selector。

    你能够用以下的技术消除它们。但这会导致自己主动俘获失效。

    不能使用自己主动俘获的情况

    要消灭编译警告,我们能够使用 NSSelectorFromString 来引用 selector。
    比如:

    CGFloat c = constant;
        SEL sel=NSSelectorFromString(@"jsq_setToolbarBottomLayoutGuideConstant:");
        if([self respondsToSelector:sel]){
            // 忽略编译器警告
            #pragma clang diagnostic push
            #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [self performSelector:sel withObject:nil];
            #pragma clang diagnostic pop
        }

    但这样的情况下。变量 c 不会被自己主动俘获。假设你在 jsq_setToolbarBottomLayoutGuideConstant: 方法的第一行代码加上断点执行程序。当程序执行到断点处时,打印參数的值,你会发现其值为 NaN 。假设继续执行代码。App 会崩溃。

    这样的情况下。我们无法使用自己主动俘获,因此仅仅能使用 withObject 来传递參数了。

    但由于 CGFloat 不是 NSObject,无法用 [performSelector: withObjectd:] 来传參。因此要使用 NSInvocation 来调用:

    -(void)setToolbarSpaceToBottom:(CGFloat)constant{
    
        SEL sel=NSSelectorFromString(@"jsq_setToolbarBottomLayoutGuideConstant:");
    
        NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:sel]];
    
        [invoc setSelector:sel];
        [invoc setTarget:self];
    
        [invoc setArgument:&constant atIndex:2];//"Indices 0 and 1 indicate the hidden arguments self and _cmd"
    
        [invoc performSelector:@selector(invoke) withObject:nil];
    }
  • 相关阅读:
    【Vue】源码——编译过程
    FreeRADIUS 、DaloRADIUS 搭建记录
    docker 学习笔记
    wget 技巧
    Centos 部署Cobbler系统
    Cloudstack 安装记录
    利用Google GCM发送push通知到Android客户端
    利用ApnsPHP包向IOS推送消息
    Linux下SCP的使用
    Android Google购买PHP服务器端验证(订阅购买和一次性购买)
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7255714.html
Copyright © 2011-2022 走看看