天天看點

performSelector的原理以及用法

一、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

繼續閱讀