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
网友评论