zoukankan      html  css  js  c++  java
  • iOS 消息处理之performSelector

    //
    //  RootViewController.h
    //  DSCategories
    //
    //  Created by dasheng on 15/12/17.
    //  Copyright © 2015年 dasheng. All rights reserved.
    //

    #import <UIKit/UIKit.h>

    @interface RootViewController : UITableViewController

    @end

    //
    //  RootViewController.m
    //  DSCategories
    //
    //  Created by dasheng on 15/12/17.
    //  Copyright © 2015年 dasheng. All rights reserved.
    //
    //performSelector和直接调用方法的区别
    //
    //performSelector: withObject:是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。所以这也是runtime的一种应用方式。
    //
    //所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。
    //但是使用performSelector的话一定是在运行时候才能发现,如果此方法不存在就会崩溃。所以如果使用performSelector的话他就会有个最佳伴侣- (BOOL)respondsToSelector:(SEL)aSelector;来在运行时判断对象是否响应此方法。

    #import "RootViewController.h"
    #import <objc/runtime.h>
    #import <objc/message.h>

    typedef struct ParameterStruct{
        int a;
        int b;
    }MyStruct;

    @interface RootViewController ()
    {
        NSArray * _sectionAry;
        NSArray * _rowAry;
        NSArray * _selAry;
    }

    @end

    @implementation RootViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
        self.title = @"Util";
        _sectionAry = @[@"PerformSelector的方法",
                        @"PerformSelector的方法延迟调用",
                        @"在子线程中无法调用selector方法"];
        
        _rowAry = @[ @[@"NoParameter",
                      @"OneParameter",
                      @"TwoParameter",
                      @"Dynamic Method",
                      @"NSInvocation",
                      @"objc_msgSend",
                      @"StructParameter",
                      @"StructParameter_Two"],
                    @[@"InBackground",
                      @"onMainThread YES",
                      @"onMainThread NO",
                      @"simple",
                      @"simple delay"],
                    @[@"No afterDelay",
                      @"AfterDelay",
                      @"AfterDelay Runloop",
                      @"dispatch_after"]];
        
        _selAry = @[ @[@"NoParameterClick",
                       @"OneParameterClick",
                       @"TwoParameterClick",
                       @"DynamicClick",
                       @"NSInvocationClick",
                       @"ObjcMsgSendClick",
                       @"StruckClick",
                       @"StruckTwoClick"],
                     @[@"inBackgroundClick",
                       @"onMainThreadWaitYesClick",
                       @"onMainThreadWaitNoClick",
                       @"simpleClick",
                       @"simpleDelayClick"],
                     @[@"NoAfterDelayClick",
                       @"AfterDelayClick",
                       @"AfterDelayRunloopClick",
                       @"dispatchAfterClick"]];
        [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
        [self.tableView reloadData];
    }

    #pragma mark - Table view data source
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return _sectionAry.count;
    }

    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
        return _sectionAry[section];
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return [_rowAry[section] count];
    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
        // Configure the cell...
        cell.textLabel.text = _rowAry[indexPath.section][indexPath.row];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        return cell;
    }

    #pragma mark - Table view delegate
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        SEL sel = NSSelectorFromString(_selAry[indexPath.section][indexPath.row]);
        [self performSelector:sel];
    }

    #pragma mark - action method

    #pragma 下面两种方法都可以用做传递多个参数用

    //传递三个及以上的参数
    - (id)performSelector:(SEL)selector withObjects:(NSArray *)objects
    {
        // 方法签名(方法的描述)
        NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
        if (signature == nil) {
            
            //可以抛出异常也可以不操作。
        }
        
        // NSInvocation : 利用一个NSInvocation对象包装一次方法调用(方法调用者、方法名、方法参数、方法返回值)
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        invocation.target = self;
        invocation.selector = selector;
        
        // 设置参数
        NSInteger paramsCount = signature.numberOfArguments - 2; // 除self、_cmd以外的参数个数
        paramsCount = MIN(paramsCount, objects.count);
        for (NSInteger i = 0; i < paramsCount; i++) {
            id object = objects[i];
            if ([object isKindOfClass:[NSNull class]]) continue;
            [invocation setArgument:&object atIndex:i + 2];
        }
        
        // 调用方法
        [invocation invoke];
        
        // 获取返回值
        id returnValue = nil;
        if (signature.methodReturnLength) { // 有返回值类型,才去获得返回值
            [invocation getReturnValue:&returnValue];
        }
        
        return returnValue;
    }

    //在主线程上调用
    - (void) performSelectorOnMainThread:(SEL)selector withObjects:(NSArray *)objects waitUntilDone:(BOOL)wait{
        
        NSMethodSignature *signature = [self methodSignatureForSelector:selector];
        if (!signature)
            return;
        NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        // 设置参数
        NSInteger paramsCount = signature.numberOfArguments - 2; // 除self、_cmd以外的参数个数
        paramsCount = MIN(paramsCount, objects.count);
        for (NSInteger i = 0; i < paramsCount; i++) {
            id object = objects[i];
            if ([object isKindOfClass:[NSNull class]]) continue;
            [invocation setArgument:&object atIndex:i + 2];
        }
        [invocation retainArguments];
        
        [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:wait];
    }

    #pragma  Click Method

    //无参数
    - (void)NoParameterClick {
        [self performSelector:@selector(SelectorNoParameter)];
    }

    //单参数
    - (void)OneParameterClick {
        [self performSelector:@selector(SelectorOneParameter:) withObject:@"firstParameter"];
    }

    //双参数
    - (void)TwoParameterClick {
        [self performSelector:@selector(SelectorFirstParameter:SecondParameter:) withObject:@"firstParameter" withObject:@"secondParameter"];
    }

    //动态添加函数
    - (void)DynamicClick {
        NSArray *objectArray = @[@{@"methodName":@"DynamicParameterString:",@"value":@"String"},@{@"methodName":@"DynamicParameterNumber:",@"value":@2}];
        for (NSDictionary *dic in objectArray) {
            [self performSelector:NSSelectorFromString([dic objectForKey:@"methodName"]) withObject:[dic objectForKey:@"value"]];
        }
    }

    //传递三个及以上的参数
    //第一种:NSInvocation
    - (void)NSInvocationClick {
        NSString *str = @"字符串";
        NSNumber *num = @20;
        NSArray *arr = @[@"数组值1", @"数组值2"];
        SEL sel = NSSelectorFromString(@"NSInvocationWithString:withNum:withArray:");
        NSArray *objs = [NSArray arrayWithObjects:str, num, arr, nil];
        //[self performSelector:sel withObjects:objs];
        //在主线程上执行方法,阻塞主线程,直到主线程将代码块执行完毕
        [self performSelectorOnMainThread:sel withObjects:objs waitUntilDone:YES];
    }

    //第三种:objc_msgSend
    - (void)ObjcMsgSendClick {
        NSString *str = @"字符串objc_msgSend";
        NSNumber *num = @20;
        NSArray *arr = @[@"数组值1", @"数组值2"];
        
        SEL sel = NSSelectorFromString(@"ObjcMsgSendWithString:withNum:withArray:");
        
        ((void (*) (id, SEL, NSString *, NSNumber *, NSArray *)) objc_msgSend) (self, sel, str, num, arr);
    }

    //传递多个参数,参数里面有结构体
    - (void)StruckClick {
        NSString *str = @"字符串 结构体";
        NSNumber *num = @20;
        NSArray *arr = @[@"数组值1", @"数组值2"];
        MyStruct mystruct = {10,20};
        
        SEL sel = NSSelectorFromString(@"ObjcMsgSendWithString:withNum:withArray:withStruck:");
        
        ((void (*) (id, SEL, NSString *, NSNumber *, NSArray *, MyStruct)) objc_msgSend) (self, sel, str, num, arr, mystruct);
    }

    //参数中有结构体
    - (void)StruckTwoClick {
        
        NSString *str = @"字符串 把结构体转换为对象";
        NSNumber *num = @20;
        NSArray *arr = @[@"数组值1", @"数组值2"];
        
        MyStruct mystruct = {10,20};
        NSValue *value = [NSValue valueWithBytes:&mystruct objCType:@encode(MyStruct)];
        
        SEL sel = NSSelectorFromString(@"NSInvocationWithString:withNum:withArray:withValue:");
        NSArray *objs = [NSArray arrayWithObjects:str, num, arr, value,nil];
        
        [self performSelector:sel withObjects:objs];
    }

    #pragma mark - Perform Selector method

    - (void)SelectorNoParameter{
        
        NSLog(@"SelectorNoParameter");
    }

    - (void)SelectorOneParameter:(NSString *)first{
        
        NSLog(@"Logs: %@", first);
    }

    - (void)SelectorFirstParameter:(NSString *)first SecondParameter:(NSString *)second{
        
        NSLog(@"Logs %@ %@", first, second);
    }

    //动态添加函数
    - (void)DynamicParameterString:(NSString *)string{
        
        NSLog(@"DynamicParameterString: %@",string);
    }

    - (void)DynamicParameterNumber:(NSNumber *)number{
        
        NSLog(@"DynamicParameterNumber: %@",number);
    }

    //多参数
    - (void)NSInvocationWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
        NSLog(@"%@, %@, %@", string, number, array[0]);
    }

    - (void)ObjcMsgSendWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
        NSLog(@"%@, %@, %@", string, number, array[0]);
    }

    //多参数有struck
    - (void)NSInvocationWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array withValue:(NSValue *)value{
        
        MyStruct struceBack;
        [value getValue:&struceBack];
        
        NSLog(@"%@, %@, %@, %d", string, number, array[0],struceBack.a);
    }

    - (void)ObjcMsgSendWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array withStruck:(MyStruct)mystruct{
        
        NSLog(@"%@, %@, %@, %d", string, number, array[0],mystruct.a);
    }

    #pragma mark - perform selector 方法延迟调用
    //这个方法是直接在后台线程运行。
    - (void)inBackgroundClick {
        [self performSelectorInBackground:@selector(delayMethod) withObject:nil];
        NSLog(@"调用方法==开始");
        sleep(5);
        NSLog(@"调用方法==结束");
    }

    //在主线程执行方法-参数wait如果为YES表示是否等待方法执行完毕再往下执行
    - (void)onMainThreadWaitYesClick {
        [self performSelectorOnMainThread:@selector(delayMethod) withObject:nil waitUntilDone:YES];
        NSLog(@"调用方法==开始");
        sleep(5);
        NSLog(@"调用方法==结束");
    }

    //在主线程执行方法-参数wait为NO,还是跟原来使用afterDelay:(NSTimeInterval)delay方式一样,要等当前调用此方法的函数执行完毕后
    - (void)onMainThreadWaitNoClick {
        [self performSelectorOnMainThread:@selector(delayMethod) withObject:nil waitUntilDone:NO];
        NSLog(@"调用方法==开始");
        sleep(5);
        NSLog(@"调用方法==结束");
    }

    //最简单的一种方式
    - (void)simpleClick {
        [self performSelector:@selector(delayMethod) withObject:nil];
        NSLog(@"调用方法==开始");
        sleep(5);
        NSLog(@"调用方法==结束");
    }

    //使用延时方法呢,可以使用dispatch_after在子线程上执行
    - (void)simpleDelayClick {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
            if ([self respondsToSelector:@selector(delayMethod)]) {
                [self performSelector:@selector(delayMethod) withObject:nil];
            }
        });
        NSLog(@"调用方法==开始");
        sleep(5);
        NSLog(@"调用方法==结束");
    }

    - (void)delayMethod {
        NSLog(@"执行selector方法");
    }

    #pragma mark - @"在子线程中无法调用selector方法"
    //在子线程中无法调用selector方法
    //
    //在子线程中无法调用selector方法这种情况是只有使用以下方法的时候才出现:
    //
    //- (void)performSelector:(SEL)aSelector withObject:(id)arg afterDelay:(NSTimeInterval)delay;
    //

    - (void)NoAfterDelayClick {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self performSelector:@selector(delayMethodLater) withObject:nil];
            NSLog(@"调用方法==开始");
            sleep(5);
            NSLog(@"调用方法==结束");
        });
    }


    - (void)AfterDelayClick {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self performSelector:@selector(delayMethodLater) withObject:nil afterDelay:0];
            NSLog(@"调用方法==开始");
            sleep(5);
            NSLog(@"调用方法==结束");
        });
    }

    //这是为什么呢?原因如下:
    //
    //1、afterDelay方式是使用当前线程的Run Loop中根据afterDelay参数创建一个Timer定时器在一定时间后调用SEL,NO AfterDelay方式是直接调用SEL。
    //
    //2、子线程中默认是没有runloop的,需要手动创建,只要调用获取当前线程RunLoop方法即可创建。
    //
    //所以解决方法有两种:
    //
    //创建子线程的runloop
    - (void)AfterDelayRunloopClick {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self performSelector:@selector(delayMethodLater) withObject:nil afterDelay:0];
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"调用方法==开始");
            sleep(5);
            NSLog(@"调用方法==结束");
        });
    }

    //使用dispatch_after在子线程上执行
    - (void)dispatchAfterClick {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
                if ([self respondsToSelector:@selector(delayMethodLater)]) {
                    [self performSelector:@selector(delayMethodLater) withObject:nil];
                }
            });
            NSLog(@"调用方法==开始");
            sleep(5);
            NSLog(@"调用方法==结束");
        });
    }

    - (void)delayMethodLater {
        NSLog(@"执行延迟方法");
    }

    @end

  • 相关阅读:
    Codeforces Round #578 (Div. 2)
    Educational Codeforces Round 70
    Codeforces Round #576 (Div. 1)
    The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest
    Educational Codeforces Round 69
    Codeforces Global Round 4
    Codeforces Round #574 (Div. 2)
    Educational Codeforces Round 68
    Codeforces Round #573 (Div. 1)
    The Preliminary Contest for ICPC China Nanchang National Invitational
  • 原文地址:https://www.cnblogs.com/yujidewu/p/6210079.html
Copyright © 2011-2022 走看看