//
//  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

iOS 消息处理之performSelector的更多相关文章

  1. iOS多线程中performSelector: 和dispatch_time的不同

    iOS中timer相关的延时调用,常见的有NSObject中的performSelector:withObject:afterDelay:这个方法在调用的时候会设置当前runloop中timer,还有 ...

  2. 消息处理之performSelector

    performSelector和直接调用方法的区别 performSelector: withObject:是在iOS中的一种方法调用方式.他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方 ...

  3. iOS多线程中performSelector

    下面两段代码都在主线程中运行,我们在看别人代码时会发现有时会直接调用,有时会利用performSelector调用,今天看到有人在问这个问题,我便做一下总结, [delegate imageDownl ...

  4. Objective-C中一种消息处理方法performSelector: withObject:

    Objective-C中调用函数的方法是“消息传递”,这个和普通的函数调用的区别是,你可以随时对一个对象传递任何消息,而不需要在编译的时候声明这些方法.所以Objective-C可以在runtime的 ...

  5. 【转】Objective-C中一种消息处理方法performSelector: withObject:

    原文 : http://www.cnblogs.com/buro79xxd/archive/2012/04/10/2440074.html   Objective-C中调用函数的方法是“消息传递”,这 ...

  6. iOS 反射函数: performSelector, NSInvocation, objc_msgSend

    当我们有方法名和参数列表,想要动态地给对象发送消息,可用通过反射函数机制来实现,有两种常用的做法: 一.performSelector - (id)performSelector:(SEL)aSele ...

  7. iOS开发之NSRunLoop的进一步理解

    http://www.cnblogs.com/pengyingh/articles/2343920.html iPhone应用开发中关于NSRunLoop的概述是本文要介绍的内容,NSRunLoop是 ...

  8. iOS事件传递&响应者链条

    原文:http://www.cnblogs.com/Quains/p/3369132.html 主要是记录下iOS的界面触摸事件处理机制,然后用一个实例来说明下应用场景. 一.处理机制 界面响应消息机 ...

  9. iOS触摸事件处理

    iOS触摸事件处理   主要是记录下iOS的界面触摸事件处理机制,然后用一个实例来说明下应用场景. 一.处理机制 界面响应消息机制分两块, (1)首先在视图的层次结构里找到能响应消息的那个视图. (2 ...

随机推荐

  1. Linux学习日记(二)

    在linux上运行.Net程序 并安装Linux网站 一.环境 ubuntu14.10(桌面版 官网下载的最新版) jexus5.6.3 正式版 MonoDevloper (安装完后里面有个Ubunt ...

  2. 通过几个Hello World感受.NET Core全新的开发体验

    2016年6月27日,这是一个特殊的日子,微软全新的.NET开发平台.NET Core的RTM版本正式发布.我个人将.NET Core的核心特性归结为三点,它们的首字母组成一个非常好记的简称——COM ...

  3. H5图片压缩与上传

    接到需求,问前端是否可以压缩图片?因为有的图片太大,传到服务器上再压缩太慢了.意识里没有这么玩过,早上老大丢来一个知乎链接,一看,原来前辈们已经用canvas实现了(为自己的见识羞愧3秒钟,再马上开干 ...

  4. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  5. .NET中使用Redis (二)

    很久以前写了一篇文章 .NET中使用Redis 介绍了如何安装Redis服务端,以及如何在.NET中调用Redis读取数据.本文简单介绍如何设计NoSQL数据库,以及如何使用Redis来存储对象. 和 ...

  6. Go语言实战 - revel框架教程之缓存和Job

    所有的网站应该都会有一个非常简单的需求,首页一秒之内打开. 满足的方式主要有两种: 页面静态化,效果最好,对服务器基本没负担,只要带宽足够就好了.我知道一个PV过亿的站点就是全站静态(以前新浪也是), ...

  7. Atitit rss没落以及替代品在线阅读器

    Atitit rss没落以及替代品在线阅读器 1.1. 对RSS的疯狂追逐,在2005年达到了一个高峰.1 1.2. Rss的问题,支持支rss,不支持url1 1.3. ,博客受到社交网络的冲击.s ...

  8. 【.net 深呼吸】聊聊WCF服务返回XML或JSON格式数据

    有时候,为了让数据可以“跨国经营”,尤其是HTTP Web有关的东东,会将数据内容以 XML 或 JSON 的格式返回,这样一来,不管客户端平台是四大文明古国,还是处于蒙昧时代的原始部落,都可以使用这 ...

  9. 前端学PHP之mysql扩展函数

    × 目录 [1]连接数据库 [2]使用数据库 [3]执行SQL查询[4]操作结果集[5]关闭连接 前面的话 mysql由于其体积小.速度快.总体拥有成本低,尤其是具有开放源码这一特点,许多中小型网站为 ...

  10. 用scikit-learn学习K-Means聚类

    在K-Means聚类算法原理中,我们对K-Means的原理做了总结,本文我们就来讨论用scikit-learn来学习K-Means聚类.重点讲述如何选择合适的k值. 1. K-Means类概述 在sc ...