一、performSelector調(diào)用和直接調(diào)用區(qū)別
下面兩段代碼都在主線程中運(yùn)行,我們在看別人代碼時會發(fā)現(xiàn)有時會直接調(diào)用,有時會利用performSelector調(diào)用,今天看到有人在問這個問題,我便做一下總結(jié),
[delegate imageDownloader:self didFinishWithImage:image];
[delegate????performSelector:@selector(imageDownloader:didFinishWithImage:)withObject:self withObject:image];
1、performSelector是運(yùn)行時系統(tǒng)負(fù)責(zé)去找方法的,在編譯時候不做任何校驗(yàn);如果直接調(diào)用編譯是會自動校驗(yàn)。如果imageDownloader:didFinishWithImage:image:不存在,那么直接調(diào)用 在編譯時候就能夠發(fā)現(xiàn)(借助Xcode可以寫完就發(fā)現(xiàn)),但是使用performSelector的話一定是在運(yùn)行時候才能發(fā)現(xiàn)(此時程序崩潰);Cocoa支持在運(yùn)行時向某個類添加方法,即方法編譯時不存在,但是運(yùn)行時候存在,這時候必然需要使用performSelector去調(diào)用。所以有時候如果使用了performSelector,為了程序的健壯性,會使用檢查方法- (BOOL)respondsToSelector:(SEL)aSelector;
2、直接調(diào)用方法時候,一定要在頭文件中聲明該方法的使用,也要將頭文件import進(jìn)來。而使用performSelector時候,可以不用import頭文件包含方法的對象,直接用performSelector調(diào)用即可。
二、常用的performSelector簡單分析
1.
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
這三個方法,均為同步執(zhí)行,與線程無關(guān),主線程和子線程中均可調(diào)用成功。等同于直接調(diào)用該方法。在需要動態(tài)的去調(diào)用方法的時候去使用。
例如:[self performSelector:@selector(test2)];與[self test2];執(zhí)行效果上完全相同。
2.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
這兩個方法為異步執(zhí)行,即使delay傳參為0,仍為異步執(zhí)行。只能在主線程中執(zhí)行,在子線程中不會調(diào)到aSelector方法。可用于當(dāng)點(diǎn)擊UI中一個按鈕會觸發(fā)一個消耗系統(tǒng)性能的事件,在事件執(zhí)行期間按鈕會一直處于高亮狀態(tài),此時可以調(diào)用該方法去異步的處理該事件,就能避免上面的問題。
在方法未到執(zhí)行時間之前,取消方法為:
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
注意:調(diào)用該方法之前或在該方法所在的viewController生命周期結(jié)束的時候去調(diào)用取消函數(shù),以確保不會引起內(nèi)存泄露。
3.
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
這兩個方法,在主線程和子線程中均可執(zhí)行,均會調(diào)用主線程的aSelector方法;
關(guān)于wait參數(shù),我試驗(yàn)的結(jié)果:
????wait為YES時,表示同步阻塞,也就是先執(zhí)行主線程的aSelector 方法,結(jié)束后再繼續(xù)執(zhí)行該線程后續(xù)代碼;
????wait為NO時,表示異步非阻塞,也就是兩個線程是并行的。
????注意:apple不允許程序員在主線程以外的線程中對ui進(jìn)行操作,此時我們必須調(diào)用????performSelectorOnMainThread函數(shù)在主線程中完成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;
調(diào)用指定線程中的某個方法。分析效果同3。
5.
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
開啟子線程在后臺運(yùn)行
三、示例
首先建立一個簡單的函數(shù)
- (void) fooNoInputs {
NSLog(@"Does nothing");
}
然后調(diào)用它[self performSelector:@selector(fooNoInputs)];
第二個試驗(yàn)看看如何在消息中傳遞參數(shù)
我們建立一個有input參數(shù)的函數(shù)
- (void) fooOneIput:(NSString*) first {
NSLog(@"Logs %@", first);
}
然后調(diào)用它[self performSelector:@selector(fooOneInput:) withObject:@"first"];
第三個試驗(yàn)更多的參數(shù)
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
NSLog(@"Logs %@ then %@", first, second);
}
然后調(diào)用它
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
第四個試驗(yàn)如何建立動態(tài)的函數(shù),然后調(diào)用他們?我們需要建立一個selector
SEL myTestSelector = @selector(myTest:);
并且我們調(diào)用的函數(shù)在另外一個Class內(nèi)
- (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會產(chǎn)生“performSelector may cause a leak because its selector is unknown”警告
2.這種方式當(dāng)傳入一個不符合約定的消息時會繼續(xù)運(yùn)行并不報錯。例如應(yīng)該傳入2個參數(shù),但只傳入1個參數(shù)。或傳入了3個參數(shù),第三個參數(shù)不會被初始化。
還有一種調(diào)用其他Class Function的方法是,但是不能有參數(shù),我們這里假設(shè)沒有參數(shù),那么就可以這樣[mfs customSelector];
完整的代碼:
@implementation ClassForSelectors
- (void) fooNoInputs {
NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
NSLog(@"Logs %@ then %@", first, second);
}
- (NSArray *)abcWithAAA: (NSNumber *)number {
int primaryKey = [number intValue];
NSLog("%i", primaryKey);
}
- (void) performMethodsViaSelectors {
[self performSelector:@selector(fooNoInputs)];
[self performSelector:@selector(fooOneInput:) withObject:@"first"];
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
- (void) performDynamicMethodsViaSelectors {
MethodForSelectors * mfs = [MethodForSelectors alloc];
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];
}
}
@end
@implementation MethodForSelectors
- (void)abcWithAAA: (NSNumber *)number {
NSLog("%i", number);
}
@end