performSelector(方法執行器),iOS中提供了以下幾種經常使用的調用方式bash
[self performSelector:@selector(sureTestMethod)];
[self performSelector:@selector(sureTestMethod)
withObject:params];
[self performSelector:@selector(sureTestMethod)
withObject:params
withObject:params2];
......
複製代碼
performSelector響應Objective-C動態性,將方法的綁定延遲到運行時,所以編譯階段不會檢測方法有效性,即方法不存在也不會提示報錯。反之由於此特性,performSelector也普遍用於動態化和組件化的模塊中。多線程
若是方法名稱也是動態不肯定的,會提示以下警告:app
SEL selector = @selector(dynamicMethod);
[self performSelector:selector];
複製代碼
⚠️ PerformSelector may cause a leak because its selector is unknown
複製代碼
意爲由於當前方法名未知可能會引發內存泄露相關問題。 能夠經過以下代碼忽略此警告async
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:selector];
#pragma clang diagnostic pop
複製代碼
performSelector默認最多隻可傳遞兩個參數,若需多參可將參數封裝爲NSArray、NSDictionary、NSInvocation進行傳遞。另外方法調用本質都是消息機制,也能夠經過msg_send實現。oop
id params;
id params2;
id params3;
SEL selector = NSSelectorFromString(@"sureTestMethod:params2:params3:");
objc_msgSend(self, selector,params,params2,params3);
- (void)sureTestMethod:(id)params params2:(id)params2 params3:(id)params3 {
NSLog(@"sureTestMethod-multi-parameter");
}
複製代碼
[self performSelector:@selector(sureTestMethod:)
withObject:params
afterDelay:3];
複製代碼
此方法意爲在當前Runloop中延遲3秒後執行selector中方法。 使用該方法須要注意如下事項: 在子線程中調用performSelector: withObject: afterDelay:默認無效,以下代碼並不會打印sureTestMethodCall組件化
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self performSelector:@selector(sureTestMethod:)
withObject:params
afterDelay:3];
});
- (void)sureTestMethod:(id)objcet {
NSLog(@"sureTestMethodCall");
}
複製代碼
這是由於performSelector: withObject: afterDelay:是在當前Runloop中延時執行的,而子線程的Runloop默認不開啓,所以沒法響應方法。ui
因此咱們嘗試在GCD Block中添加 [[NSRunLoop currentRunLoop]run];spa
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self performSelector:@selector(sureTestMethod:)
withObject:params
afterDelay:3];
[[NSRunLoop currentRunLoop]run];
});
複製代碼
運行代碼發現能夠正常打印sureTestMethodCall。線程
這裏有個坑須要注意,曾經嘗試將 [[NSRunLoop currentRunLoop]run]添加在performSelector: withObject: afterDelay:方法前,但發現延遲方法仍然不調用,這是由於若想開啓某線程的Runloop,必須具備timer、source、observer任一事件才能觸發開啓。code
簡言之以下代碼在執行 [[NSRunLoop currentRunLoop]run]前沒有任何事件添加到當前Runloop,所以該線程的Runloop是不會開啓的,從而延遲事件不執行。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[[NSRunLoop currentRunLoop]run];
[self performSelector:@selector(sureTestMethod:)
withObject:params
afterDelay:3];
});
複製代碼
關於Runloop,可詳見:深刻理解RunLoop
咱們在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");
}
複製代碼
重複點擊後,打印結果以下,只響應了一次點擊
2019-05-06 11:29:50.352157+0800 performSelector[14342:457353] sureTestMethodCall
複製代碼
咱們能夠經過performSelectorInBackground將某selector任務放在子線程中
[self performSelectorInBackground:@selector(sureTestMethod:)
withObject:params];
- (void)sureTestMethod:(id)objcet {
NSLog(@"%@",[NSThread currentThread]);
}
複製代碼
//<NSThread: 0x600003854080>{number = 3, name = (null)}
複製代碼
打印結果可見當前方法運行在子線程中。
回到主線程執行咱們能夠經過方法
[self performSelectorOnMainThread:@selector(sureTestMethod)
withObject:params
waitUntilDone:NO];
複製代碼
waitUntilDone表示是否等待當前selector任務完成後再執行後續任務。示例以下,waitUntilDone爲YES時,打印1,2,3。爲NO時打印1,3,2。
NSLog(@"1");
[self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:NO];
NSLog(@"3");
複製代碼
- (void)test {
sleep(3);
NSLog(@"2");
}
複製代碼
另外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已不存在致使崩潰。
好了,關於performSelector的內容暫時寫到這裏了,有其餘補充歡迎評論~