performSelector和直接调用方法的区别

performSelector: withObject:是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。所以这也是runtime的一种应用方式.所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。

但是使用performSelector的话一定是在运行时候才能发现,如果此方法不存在就会崩溃。所以如果使用performSelector的话他就会有个最佳伴侣- (BOOL)respondsToSelector:(SEL)aSelector;来在运行时判断对象是否响应此方法。

performSelector的使用

第一种:无参数传递

[self performSelector:@selector(SelectorNoParameter)];

- (void)SelectorNoParameter{

    NSLog(@"SelectorNoParameter");
}

第二种:传递一个参数

[self performSelector:@selector(SelectorOneParameter:) withObject:@"firstParameter"];

- (void)SelectorOneParameter:(NSString *)first{

    NSLog(@"Logs: %@", first);
} ####第三种:传递两个参数 [self performSelector:@selector(SelectorFirstParameter:SecondParameter:) withObject:@"firstParameter" withObject:@"secondParameter"]; - (void)SelectorFirstParameter:(NSString *)first SecondParameter:(NSString *)second{ NSLog(@"Logs %@ %@", first, second);
}

第四种:建立动态的函数,然后调用它们

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"]];
} - (void)DynamicParameterString:(NSString *)string{ NSLog(@"DynamicParameterString: %@",string);
} - (void)DynamicParameterNumber:(NSNumber *)number{ NSLog(@"DynamicParameterNumber: %@",number);
}

这里还有一些其他的类似的方法:

performSelectorOnMainThread:@selector() withObjects:object waitUntilDone:YES

这个函数表示在主线程上执行方法,YES表示需要阻塞主线程,知道主线程将我们的代码块执行完毕。

performSelector相关的应用:

没有实际应用的知识讲解都是耍流氓

传递三个及以上的参数

performSelector如何传递三个及以上的参数。

这里有三种方法,一种是使用NSInvocation,还有一种是把多个参数封装成一个参数。第三种是使用objc_msgSend,应该不算用performSelector这种方式,但是performSelector最后也还是用objc_msgSend这个方法进行消息转发的,所以姑且也写进来了。

第一种使用了runtime的反射机制,效率较低,可读性不高。第二种可读性高,效率稍微高点。

第一种:NSInvocation

- (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;
} 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]; - (void)NSInvocationWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
NSLog(@"%@, %@, %@", string, number, array[0]);
}

第二种:把多个参数封装成一个参数

比如可以把多个参数封装成NSDictionary,然后进行传递。

第三种:objc_msgSend

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)ObjcMsgSendWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
NSLog(@"%@, %@, %@", string, number, array[0]);
}

参数中有结构体

如果传入performSelector中的多个参数有结构体怎么办?

可以把结构体转换为对象。

typedef struct ParameterStruct{
int a;
int b;
}MyStruct; 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]; - (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);
}

aSelector方法被延迟调用

performSelector里的aSelector方法被延迟调用问题。

- (void)performSelector:(SEL)aSelector withObject:(id)arg; afterDelay:(NSTimeInterval)delay;

这个方法其实是增加了一个定时器,而这时aSelector应该是被添加到了队列的最后面,所以要等当前调用此方法的函数执行完毕后,selector方法才会执行。如下:

- (void)mainMethod
{
[self performSelector:@selector(delayMethod) withObject:nil afterDelay:1]; NSLog(@"调用方法==开始"); sleep(5); NSLog(@"调用方法==结束");
} - (void)delayMethod
{
NSLog(@"执行延迟方法");
}

执行结果(注意log打印的顺序):

调用方法==开始

调用方法==结束

执行延迟方法

那如果我要selector方法不用等待函数执行完再执行怎么办?其实只要不使用有afterDelay:(NSTimeInterval)delay这个参数的方法就可以了。

- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

这个方法是直接在后台线程运行。如果直接用这个上面例子的打印结果就是:

执行结果(注意log打印的顺序):

 调用方法==开始

执行延迟方法

调用方法==结束

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

这个方法是表示在主线程执行方法。然后参数wait如果为YES表示是否等待方法执行完毕再往下执行。那输出结果就是:

执行结果(注意log打印的顺序):

执行延迟方法

调用方法==开始

调用方法==结束

如果为NO,还是跟原来使用afterDelay:(NSTimeInterval)delay方式一样,要等当前调用此方法的函数执行完毕后,selector方法才会执行,输出结果就是:

执行结果(注意log打印的顺序):

调用方法==开始

调用方法==结束

执行延迟方法

- (id)performSelector:(SEL)aSelector withObject:(id)object;

这种是最简单的一种方式,输出结果是:

执行结果(注意log打印的顺序):

执行延迟方法

调用方法==开始

调用方法==结束

但如果我要使用延时方法呢,可以使用dispatch_after在子线程上执行:

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(@"调用方法==结束");

输出结果如下:

执行结果(注意log打印的顺序):

调用方法==开始

执行延迟方法

调用方法==结束

在子线程中无法调用selector方法

在子线程中无法调用selector方法这种情况是只有使用以下方法的时候才出现:

- (void)performSelector:(SEL)aSelector withObject:(id)arg afterDelay:(NSTimeInterval)delay;

这是为什么呢?原因如下:

1、afterDelay方式是使用当前线程的Run Loop中根据afterDelay参数创建一个Timer定时器在一定时间后调用SEL,NO AfterDelay方式是直接调用SEL。

2、子线程中默认是没有runloop的,需要手动创建,只要调用获取当前线程RunLoop方法即可创建。

所以解决方法有两种:

创建子线程的runloop
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self performSelector:@selector(delayMethod) withObject:nil afterDelay:0];
[[NSRunLoop currentRunLoop] run];
NSLog(@"调用方法==开始");
sleep(5);
NSLog(@"调用方法==结束");
}); 使用dispatch_after在子线程上执行 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(@"调用方法==结束");

防止按钮多次点击

这种方式是在0.2秒内取消之前的点击事件,以做到防止多次点击。

-(void)completeClicked:(UIButton *)sender{
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(buttonClick:) object:sender];
[self performSelector:@selector(buttonClick:) withObject:sender afterDelay:0.2f];
}

这种方式是在点击后设为不可被点击的状态,1秒后恢复

-(void)buttonClicked:(id)sender{

    self.button.enabled =NO;

    [selfperformSelector:@selector(changeButtonStatus)withObject:nilafterDelay:1.0f];//防止重复点击

}

-(void)changeButtonStatus{

    self.button.enabled =YES;
}

文/齐滇大圣(简书作者)

原文链接:http://www.jianshu.com/p/672c0d4f435a

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

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

  1. iOS 消息处理之performSelector

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

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

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

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

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

  4. NSObject类的API介绍

    这篇文章围绕的对象就是NSObject.h文件,对声明文件中的属性.方法进行必要的“翻译”. 该文件大致由两部分组成:NSObject协议和NSObject类. (一)NSObject协议 - (BO ...

  5. [Xcode 实际操作]八、网络与多线程-(18)PerformSelector消息处理方法:由运行时系统,负责去调用对象的指定方法

    目录:[Swift]Xcode实际操作 本文将演示PerformSelector消息处理方法. 在项目文件夹上点击鼠标右键弹出文件菜单. [New File]->[Swift File]-> ...

  6. iOS 网络与多线程--7.Performselector消息处理方法

    创建一个IOSApp类 IOSApp.h文件 #import <Foundation/Foundation.h> @interface IOSApp : NSObject // 1.添加一 ...

  7. Storm如何保证可靠的消息处理

    作者:Jack47 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文主要翻译自Storm官方文档Guaranteeing messag ...

  8. ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理

    系列目录 前言 Senparc.Weixin.MP SDK提供了MessageHandler消息处理类 在作者的Wiki中也详细说明了如何定义这个类,下面我们来演示,消息的回复,及效果 了解Messa ...

  9. Android消息处理机制(Handler、Looper、MessageQueue与Message)

    Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取 ...

随机推荐

  1. 小窍门:变更Windows Azure Websites自带的node.exe版本

    这几天在玩node.js.Azure Websites天然支持node.js(还支持.net, php和python).   它对nodejs支持的原理是: IIS充当Web服务器,接收所有的请求,而 ...

  2. ME525+ 刷机工具及设置中心号码

    接上篇: 刷机包下载地址http://sbf.droid-developers.org/umts_jordanplus/list.php,选择一款大陆包.... 设置中心号码: 拨打   *#*#46 ...

  3. div简单布局理解

    以下是div的理解

  4. The Tips of Success(成功的建议)

    1.Do one thing at a time,and do well. 2.Never forget to say "thanks". 3,Keep on going.Neve ...

  5. wcf系列学习5天速成——第四天 wcf之分布式架构

    今天是wcf系列的第四天,也该出手压轴戏了.嗯,现在的大型架构,都是神马的, nginx鸡群,iis鸡群,wcf鸡群,DB鸡群,由一个人作战变成了群殴....... 今天我就分享下wcf鸡群,高性能架 ...

  6. document.body is null

    document.body is null:做前端的同学们对这个错误应该不陌生吧 出现这个问题的原因是:你太着急了,document还没渲染到body呢,你就想调用了,当然会找不到了 解决办法so e ...

  7. 2014.12.01 B/S之windows8.1下安装IIS

    1.打开 控制面板——程序——程序和功能——启用或关闭windows功能 2.找到Internet信息服务         3.等待安装完毕即可 4.控制面板——系统和安全——管理工具——Intern ...

  8. Docker的C/S模式详解

    Docker的C/S模式 Docker的C/S模式 Docker Client通过Remote API与Docker Server通信: RESTful风格API STDIN.STDOUT.STDER ...

  9. HTML DOM 属性记录

    将HTML DOM中几个容易常用的属性做下记录,需要的朋友可以参考下.     nodeName.nodeValue 以及 nodeType 包含有关于节点的信息. nodeName 属性含有某个节点 ...

  10. codeforces 665D Simple Subset

    题目链接 给一个数列, 让你选出其中的m个数, 使得选出的数中任意两个数之和都为质数, m尽可能的大. 首先, 除了1以外的任意两个相同的数相加结果都不是质数. 然后, 不考虑1的话, 选出的数的个数 ...