swift - perform

作者: 大也 | 来源:发表于2021-07-05 08:54 被阅读0次

    1.知识补充
    那么直接调用没有定义的方法:在编译时候就能够发现(借助Xcode可以写完就发现),但是使用performSelector的话一定是在运行时候才能发现(此时程序崩溃);

    Cocoa支持在运行时向某个类添加方法,即方法编译时不存在,但是运行时候存在,这时候必然需要使用performSelector去调用。

    所以有时候如果使用了performSelector,为了程序的健壮性,会使用检查方法respondsToSelector。

    直接调用方法时候,一定要在头文件中声明该方法的使用,也要将头文件import进来。而使用performSelector时候, 可以不用import头文件包含方法的对象,直接用performSelector调用即可。

    performSelector是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。所以这也是runtime的一种应用方式。
    所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。
    但是使用performSelector的话一定是在运行时候才能发现,如果此方法不存在就会崩溃。所以如果使用performSelector的话他就会有个最佳伴侣respondsToSelector:;来在运行时判断对象是否响应此方法。

    备注:runtime
    在这小作总结:OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法。利用runtime机制让我们可以在程序运行时动态修改类、对象中的所有属性、方法,就算是私有方法以及私有属性都是可以动态修改的。

    2.swift 方法

    例子1
    //调用方法
    perform(<#T##aSelector: Selector!##Selector!#>) 
    //调用方法 1个参数
          perform(<#T##aSelector: Selector!##Selector!#>, with: <#T##Any!#>)
    //调用方法 2个参数
          perform(<#T##aSelector: Selector!##Selector!#>, with: <#T##Any!#>, with: <#T##Any!#>)
    
    例子2
    //调用方法 延迟调用 
    //注意:使用该方法需要注意以下事项: 在子线程中调用performSelector: withObject: afterDelay:默认无效
    这是因为performSelector: withObject: afterDelay:是在当前Runloop中延时执行的,而子线程的Runloop默认不开启,因此无法响应方法 除非再子线程中添加 [[NSRunLoop currentRunLoop]run];
    //        perform(#selector(alertStyleTitleOnly), with: nil, afterDelay: 0.0, inModes: [RunLoop.Mode.default])
    //        perform(#selector(方法), with: 参数, afterDelay: 延迟时间, inModes: 线程数组)
          perform(<#T##aSelector: Selector##Selector#>, with: <#T##Any?#>, afterDelay: <#T##TimeInterval#>)
          perform(<#T##aSelector: Selector##Selector#>, with: <#T##Any?#>, afterDelay: <#T##TimeInterval#>, inModes: <#T##[RunLoop.Mode]#>)
    
    例子3 performSelector取消延迟
          ADFunctionTableVC.cancelPreviousPerformRequests(withTarget: <#T##Any#>)
          ADFunctionTableVC.cancelPreviousPerformRequests(withTarget: <#T##Any#>, selector: <#T##Selector#>, object: <#T##Any?#>)
    
    例子4
    waitUntilDone表示是否等待当前selector任务完成后再执行后续任务。示例如下,waitUntilDone为YES时,打印1,2,3。为NO时打印1,3,2。
    注意 modes 一直找不到它的解释 暂时无法记忆
    //        perform(#selector(alertStyleTitleOnly), on: Thread.current, with: nil, waitUntilDone: false)
    
          perform(<#T##aSelector: Selector##Selector#>, on: <#T##Thread#>, with: <#T##Any?#>, waitUntilDone: <#T##Bool#>)
          perform(<#T##aSelector: Selector##Selector#>, on: <#T##Thread#>, with: <#T##Any?#>, waitUntilDone: <#T##Bool#>, modes: <#T##[String]?#>)
    
    例子5
    注意 modes 一直找不到它的解释 暂时无法记忆
    //        performSelector(onMainThread: #selector(alertStyleTitleOnly), with: nil, waitUntilDone: true)
            performSelector(inBackground: <#T##Selector#>, with: <#T##Any?#>)
            performSelector(onMainThread: <#T##Selector#>, with: <#T##Any?#>, waitUntilDone: <#T##Bool#>)
            performSelector(onMainThread: <#T##Selector#>, with: <#T##Any?#>, waitUntilDone: <#T##Bool#>, modes: <#T##[String]?#>)
    

    备注 在swift中 使用 调用的方法要加@objc

    ----------------例子

    例子1
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
     
        /*performSelector
         - (id)performSelector:(SEL)aSelector;
         - (id)performSelector:(SEL)aSelector withObject:(id)object;
         - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
         
          - (BOOL)respondsToSelector:(SEL)aSelector;
         */
        
        //其次,进行调用
        //没有参数
        BOOL isNoParam= [self respondsToSelector:@selector(methodNoParam)];
        if (isNoParam) {
            [self performSelector:@selector(methodNoParam)];
        }
        
        //一个参数
        BOOL isOneParam= [self respondsToSelector:@selector(methodWithOneParam:)];
        if (isOneParam) {
            [self performSelector:@selector(methodWithOneParam:) withObject:@"first"];
        }
        
        //二个参数
        BOOL isParams= [self respondsToSelector:@selector(methodWithParams: andParamSecond:)];
        if (isParams) {
            [self performSelector:@selector(methodWithParams: andParamSecond:) withObject:@"first" withObject:@"second"];
        }
    
        //建立动态的函数,然后调用它们
        NSArray *objectArray = @[@{@"methodName":@"DynamicParameterString:", @"value":@"String"},
                               @{@"methodName":@"DynamicParameterNumber:", @"value":@2}];
        for (NSDictionary *dic in objectArray) {
            SEL selector = NSSelectorFromString([dic objectForKey:@"methodName"]);
            if ([self respondsToSelector:selector]) {
                //这里会有警告:PerformSelector may cause a leak because its selector is unknown
                [self performSelector:selector withObject:[dic objectForKey:@"value"]];
            }
        }
        
        //PerformSelector may cause a leak because its selector is unknown 解决方法
        //1.使用函数指针方式
        for (NSDictionary *dic in objectArray) {
            SEL selector = NSSelectorFromString([dic objectForKey:@"methodName"]);
            if ([self respondsToSelector:selector]){
                IMP imp = [self methodForSelector:selector];
                void (*func)(id, SEL) = (void *)imp;
                func(self, selector);
            }
        }
        //其它方法消除警告:http://www.tuicool.com/articles/iu6zuu
    
    }
    
    //首先,定义要调用的方法
    - (void)methodNoParam{
        NSLog(@"methodNoParam");
    }
    
    - (void)methodWithOneParam:(id)paramFirst{
        NSLog(@"methodWithOneParam: %@", paramFirst);
    }
    
    - (void)methodWithParams:(id)paramFirst andParamSecond:(id) paramSecond{
        NSLog(@"methodWithOneParam: %@,%@", paramFirst,paramSecond);
    }
    
    
    - (void)DynamicParameterString:(NSString *)string{
        NSLog(@"DynamicParameterString: %@",string);
    }
    
    - (void)DynamicParameterNumber:(NSNumber *)number{
        NSLog(@"DynamicParameterNumber: %@",number);
    }
    
    例子2
    //子线程 不打印
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self performSelector:@selector(sureTestMethod:)
                   withObject:params
                   afterDelay:3];
    });
    - (void)sureTestMethod:(id)objcet {
        NSLog(@"sureTestMethodCall");
    }
    
    //正常
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self performSelector:@selector(sureTestMethod:)
                       withObject:params
                       afterDelay:3];
            [[NSRunLoop currentRunLoop]run];
        });
    
    这里有个坑需要注意,曾经尝试将 [[NSRunLoop currentRunLoop]run]添加在performSelector: withObject: afterDelay:方法前,但发现延迟方法仍然不调用,这是因为若想开启某线程的Runloop,必须具有timer、source、observer任一事件才能触发开启。
    
    简言之如下代码在执行 [[NSRunLoop currentRunLoop]run]前没有任何事件添加到当前Runloop,因此该线程的Runloop是不会开启的,从而延迟事件不执行。
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [[NSRunLoop currentRunLoop]run];
            [self performSelector:@selector(sureTestMethod:)
                       withObject:params
                       afterDelay:3];
        });
    
    例子3、performSelector取消延迟
    
    我们在View上放置一个Button,预期需求是防止暴力点击,只响应最后一次点击时的事件。
    
    此需求我们可以通过cancelPreviousPerformRequestsWithTarget来进行实现。cancelPreviousPerformRequestsWithTarget的作用为取消当前延时任务。在执行延迟事件前取消当前存在的延迟任务即可实现如上效果。
    
    - (IBAction)buttonClick:(id)sender {
        id params;
        [[self class]cancelPreviousPerformRequestsWithTarget:self
                                                    selector:@selector(sureTestMethod:)
                                                      object:params];
        [self performSelector:@selector(sureTestMethod:)
                   withObject:params
                   afterDelay:3];
    }
     
    - (void)sureTestMethod:(id)objcet {
        NSLog(@"sureTestMethodCall");
    }
    重复点击后,打印结果如下,只响应了一次点击
    performSelector[14342:457353] sureTestMethodCall
    
    例子4
    
      NSLog(@"1");
        [self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:NO];
        NSLog(@"3");
    
    - (void)test {
        sleep(3);
        NSLog(@"2");
    }
    
    waitUntilDone表示是否等待当前selector任务完成后再执行后续任务。示例如下,waitUntilDone为YES时,打印1,2,3。为NO时打印1,3,2。
    我们在View上放置一个Button,预期需求是防止暴力点击,只响应最后一次点击时的事件。
    
    另外performSelector还提供了将任务执行在某个指定线程的操作
    [self performSelector:@selector(sureTestMethod:)
                     onThread:thread
                   withObject:params
                waitUntilDone:NO];
    复制代码
    使用该方法一定要注意所在线程生命周期是否正常,若thread已销毁不存在,而performSelector强行执行任务在该线程,会导致崩溃:
    
    NSThread *thread = [[NSThread alloc]initWithBlock:^{
        NSLog(@"do thread event");
    }];
    [thread start];
    [self performSelector:@selector(sureTestMethod:)
                 onThread:thread
               withObject:params
            waitUntilDone:YES];
    复制代码
    上述代码会导致崩溃,崩溃信息为:
    
    *** Terminating app due to uncaught exception 'NSDestinationInvalidException',
    reason: '*** -[ViewController performSelector:onThread:withObject:waitUntilDone:modes:]:
    target thread exited while waiting for the perform'
    复制代码
    因为thread开启执行do thread event完毕后即退出销毁,所以在等待执行任务时Thread已不存在导致崩溃。
    
    
    例子5
    
    我们可以通过performSelectorInBackground将某selector任务放在子线程中
    
    [self performSelectorInBackground:@selector(sureTestMethod:)
                               withObject:params];
    - (void)sureTestMethod:(id)objcet {
        NSLog(@"%@",[NSThread currentThread]);
    }
    
    回到主线程执行我们可以通过方法
    
    [self performSelectorOnMainThread:@selector(sureTestMethod)
                           withObject:params
                        waitUntilDone:NO];
    

    https://blog.csdn.net/weixin_33720956/article/details/91364175
    https://www.jianshu.com/p/672c0d4f435a

    相关文章

      网友评论

        本文标题:swift - perform

        本文链接:https://www.haomeiwen.com/subject/syweultx.html