在项目中,常常用到performSelector之类的方法,而且有很多类似方法;今天就来召集一下!
第一点 performSelector的使用
- (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);
}
这里需要补充的知识:关于performSelector调用和直接调用方法的区别
1、performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。
如果methodNoParam、methodWithOneParam:、methodWithParams: andParamSecond:不存在
那么直接调用:在编译时候就能够发现(借助Xcode可以写完就发现),但是使用performSelector的话一定是在运行时候才能发现(此时程序崩溃);
Cocoa支持在运行时向某个类添加方法,即方法编译时不存在,但是运行时候存在,这时候必然需要使用performSelector去调用。
所以有时候如果使用了performSelector,为了程序的健壮性,会使用检查方法respondsToSelector。
2、直接调用方法时候,一定要在头文件中声明该方法的使用,也要将头文件import进来。而使用performSelector时候, 可以不用import头文件包含方法的对象,直接用performSelector调用即可。
3、performSelector是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。所以这也是runtime的一种应用方式。
所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。
但是使用performSelector的话一定是在运行时候才能发现,如果此方法不存在就会崩溃。所以如果使用performSelector的话他就会有个最佳伴侣respondsToSelector:;来在运行时判断对象是否响应此方法。
备注:runtime???
在这小作总结:OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法。利用runtime机制让我们可以在程序运行时动态修改类、对象中的所有属性、方法,就算是私有方法以及私有属性都是可以动态修改的。
// 动态添加方法
// 开发场景:如果一个类方法非常多,加载了到内存的时候也比较耗费资源,需给每个方法生成映射表,可以使用动态给某个类,添加方法解决.
// 经典面试题:有没有使用preformSelector,其实主要想问有没有添加过方法;
performSelector相关的应用:传递三个及以上的参数
第一种:NSInvocation
第二种:把多个参数封装成一个参数
在这里不做描述:详情参考 https://my.oschina.net/ososchina/blog/644117
第三种: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]);
}
参数中有结构体的处理方式
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);
}
第二点
/**
在主线程上执行指定的方法,使用默认的模式(NSDefaultRunLoopMode)
默认的模式指:主线程中的方法进行排队,是一个循环队列,并且循环执行。
这个函数表示在主线程上执行方法,YES表示需要阻塞主线程,直到主线程将我们的代码块执行完毕。
@param aSelector 要在主线程执行的方法,该方法不能有返回值,并且只能有一个参数。
@param arg 要传递的参数,如果无参数,就设为nil。
@param wait 要执行的aSelector方法,是否马上执行。
如果设置为YES:等待当前线程执行完以后,主线程才会执行aSelector方法;
设置为NO:不等待当前线程执行完,就在主线程上执行aSelector方法。
如果,当前线程就是主线程,那么aSelector方法会马上执行。
该方法用途:因为iPhone编程,对UI的修改,只能在主线程上执行。可以用该方法来完成UI的修改。
*/
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait {
NSLog(@"用的多");
}
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array {
}
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait {
}
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array {
}
/**
在当前线程中执行指定的方法,使用默认模式,并指定延迟。
@param aSelector 指定的方法。含义同上,不在赘述。
@param anArgument 同上
@param delay 指定延迟时间(秒)。
*/
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay {
NSLog(@"用的多");
}
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes {
}
这里需要补充的知识:
Runloop 相当于 win32 里面的消息循环机制,它可以让你根据事件/消息(鼠标消息,键盘消息,触摸事件,计时器消息等)来调度线程。
比如:在触摸 UIView 时之所以能够激发 touchesBegan/touchesMoved 等等函数被调用。系统会自动为应用程序的主线程生成一个与之对应的 run loop 来处理其消息循环。让调用更加简单。也避免了繁琐,复杂的操作。
一句话:Runloop是一种消息处理机制!
参考链接:
//www.greatytc.com/p/672c0d4f435a
http://blog.sina.com.cn/s/blog_7b9d64af0101bjw4.html
http://blog.csdn.net/meegomeego/article/details/20041887