一、performSelector調用和直接調用差別
下面兩段代碼都在主線程中運作,我們在看别人代碼時會發現有時會直接調用,有時會利用performSelector調用,今天看到有人在問這個問題,我便做一下總結,
[delegate imageDownloader:self didFinishWithImage:image];
[delegate performSelector:@selector(imageDownloader:didFinishWithImage:)withObject:self withObject:image];
1、performSelector是運作時系統負責去找方法的,在編譯時候不做任何校驗;如果直接調用編譯是會自動校驗。如果imageDownloader:didFinishWithImage:image:不存在,那麼直接調用 在編譯時候就能夠發現(借助Xcode可以寫完就發現),但是使用performSelector的話一定是在運作時候才能發現(此時程式崩潰);Cocoa支援在運作時向某個類添加方法,即方法編譯時不存在,但是運作時候存在,這時候必然需要使用performSelector去調用。是以有時候如果使用了performSelector,為了程式的健壯性,會使用檢查方法- (BOOL)respondsToSelector:(SEL)aSelector;
2、直接調用方法時候,一定要在頭檔案中聲明該方法的使用,也要将頭檔案import進來。而使用performSelector時候,可以不用import頭檔案包含方法的對象,直接用performSelector調用即可。
二、常用的performSelector簡單分析
1.
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
這三個方法,均為同步執行,與線程無關,主線程和子線程中均可調用成功。等同于直接調用該方法。在需要動态的去調用方法的時候去使用。
例如:[self performSelector:@selector(test2)];與[self test2];執行效果上完全相同。
2.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
這兩個方法為異步執行,即使delay傳參為0,仍為異步執行。隻能在主線程中執行,在子線程中不會調到aSelector方法。可用于當點選UI中一個按鈕會觸發一個消耗系統性能的事件,在事件執行期間按鈕會一直處于高亮狀态,此時可以調用該方法去異步的處理該事件,就能避免上面的問題。
在方法未到執行時間之前,取消方法為:
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
注意:調用該方法之前或在該方法所在的viewController生命周期結束的時候去調用取消函數,以確定不會引起記憶體洩露。
3.
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
這兩個方法,在主線程和子線程中均可執行,均會調用主線程的aSelector方法;
如果設定wait為YES:等待目前線程執行完以後,主線程才會執行aSelector方法;
設定為NO:不等待目前線程執行完,就在主線程上執行aSelector方法。
如果,目前線程就是主線程,那麼aSelector方法會馬上執行。
注意:apple不允許程式員在主線程以外的線程中對ui進行操作,此時我們必須調用performSelectorOnMainThread函數在主線程中完成UI的更新。
4.
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
調用指定線程中的某個方法。分析效果同3。
5.
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
開啟子線程在背景運作
三、示例
首先建立一個簡單的函數
- (void) fooNoInputs {
NSLog(@"Does nothing");
}
然後調用它[self performSelector:@selector(fooNoInputs)];
第二個試驗看看如何在消息中傳遞參數
我們建立一個有input參數的函數
- (void) fooOneIput:(NSString*) first {
NSLog(@"Logs %@", first);
然後調用它[self performSelector:@selector(fooOneInput:) withObject:@"first"];
第三個試驗更多的參數
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
NSLog(@"Logs %@ then %@", first, second);
然後調用它
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
第四個試驗如何建立動态的函數,然後調用他們?我們需要建立一個selector
SEL myTestSelector = @selector(myTest:);
并且我們調用的函數在另外一個Class内
- (void)abcWithAAA: (NSNumber *)number {
int primaryKey = [number intValue];
NSLog("%i", primaryKey);
MethodForSelectors * mfs = [[MethodForSelectors alloc]init];
NSArray *Arrays = [NSArray arrayWithObjects:@"AAA", @"BBB", nil];
for ( NSString *array in Arrays ){
SEL customSelector = NSSelectorFromString([NSStringstringWithFormat:@"abcWith%@:", array]);
mfs = [[MethodForSelectors alloc] performSelector:customSelector withObject:0];
注意:updated at 20120606
1.如果使用了ARC會産生“performSelector may cause a leak because its selector is unknown”警告
2.這種方式當傳入一個不符合約定的消息時會繼續運作并不報錯。例如應該傳入2個參數,但隻傳入1個參數。或傳入了3個參數,第三個參數不會被初始化。
還有一種調用其他Class Function的方法是,但是不能有參數,我們這裡假設沒有參數,那麼就可以這樣[mfs customSelector];
完整的代碼:
@implementation ClassForSelectors
- (NSArray *)abcWithAAA: (NSNumber *)number {
- (void) performMethodsViaSelectors {
[self performSelector:@selector(fooNoInputs)];
[self performSelector:@selector(fooOneInput:) withObject:@"first"];
- (void) performDynamicMethodsViaSelectors {
MethodForSelectors * mfs = [MethodForSelectors alloc];
@end
@implementation MethodForSelectors
NSLog("%i", number);
@end