在項目中淘衙,常常用到performSelector之類的方法鹦筹,而且有很多類似方法;今天就來召集一下消玄!
第一點 performSelector的使用
- (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;
*/
//其次薯演,進行調(diào)用
//沒有參數(shù)
BOOL isNoParam= [self respondsToSelector:@selector(methodNoParam)];
if (isNoParam) {
[self performSelector:@selector(methodNoParam)];
}
//一個參數(shù)
BOOL isOneParam= [self respondsToSelector:@selector(methodWithOneParam:)];
if (isOneParam) {
[self performSelector:@selector(methodWithOneParam:) withObject:@"first"];
}
//二個參數(shù)
BOOL isParams= [self respondsToSelector:@selector(methodWithParams: andParamSecond:)];
if (isParams) {
[self performSelector:@selector(methodWithParams: andParamSecond:) withObject:@"first" withObject:@"second"];
}
//建立動態(tài)的函數(shù)撞芍,然后調(diào)用它們
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.使用函數(shù)指針方式
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
}
//首先,定義要調(diào)用的方法
- (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);
}
這里需要補充的知識:關(guān)于performSelector調(diào)用和直接調(diào)用方法的區(qū)別
1跨扮、performSelector是運行時系統(tǒng)負責(zé)去找方法的序无,在編譯時候不做任何校驗;如果直接調(diào)用編譯是會自動校驗衡创。
如果methodNoParam愉镰、methodWithOneParam:、methodWithParams: andParamSecond:不存在
那么直接調(diào)用:在編譯時候就能夠發(fā)現(xiàn)(借助Xcode可以寫完就發(fā)現(xiàn))钧汹,但是使用performSelector的話一定是在運行時候才能發(fā)現(xiàn)(此時程序崩潰)丈探;
Cocoa支持在運行時向某個類添加方法,即方法編譯時不存在拔莱,但是運行時候存在碗降,這時候必然需要使用performSelector去調(diào)用。
所以有時候如果使用了performSelector塘秦,為了程序的健壯性讼渊,會使用檢查方法respondsToSelector。
2尊剔、直接調(diào)用方法時候爪幻,一定要在頭文件中聲明該方法的使用,也要將頭文件import進來须误。而使用performSelector時候挨稿, 可以不用import頭文件包含方法的對象,直接用performSelector調(diào)用即可京痢。
3奶甘、performSelector是在iOS中的一種方法調(diào)用方式。他可以向一個對象傳遞任何消息祭椰,而不需要在編譯的時候聲明這些方法臭家。所以這也是runtime的一種應(yīng)用方式。
所以performSelector和直接調(diào)用方法的區(qū)別就在與runtime方淤。直接調(diào)用編譯是會自動校驗钉赁。如果方法不存在,那么直接調(diào)用 在編譯時候就能夠發(fā)現(xiàn)携茂,編譯器會直接報錯你踩。
但是使用performSelector的話一定是在運行時候才能發(fā)現(xiàn),如果此方法不存在就會崩潰。所以如果使用performSelector的話他就會有個最佳伴侶respondsToSelector:;來在運行時判斷對象是否響應(yīng)此方法姓蜂。
備注:runtime?医吊?钱慢?
在這小作總結(jié):OC是運行時語言,只有在程序運行時卿堂,才會去確定對象的類型束莫,并調(diào)用類與對象相應(yīng)的方法。利用runtime機制讓我們可以在程序運行時動態(tài)修改類草描、對象中的所有屬性览绿、方法,就算是私有方法以及私有屬性都是可以動態(tài)修改的穗慕。
// 動態(tài)添加方法
// 開發(fā)場景:如果一個類方法非常多,加載了到內(nèi)存的時候也比較耗費資源,需給每個方法生成映射表,可以使用動態(tài)給某個類,添加方法解決.
// 經(jīng)典面試題:有沒有使用preformSelector,其實主要想問有沒有添加過方法;
performSelector相關(guān)的應(yīng)用:傳遞三個及以上的參數(shù)
第一種:NSInvocation
第二種:把多個參數(shù)封裝成一個參數(shù)
在這里不做描述:詳情參考 https://my.oschina.net/ososchina/blog/644117
第三種:objc_msgSend
NSString *str = @"字符串objc_msgSend";
NSNumber *num = @20;
NSArray *arr = @[@"數(shù)組值1", @"數(shù)組值2"];
SEL sel = NSSelectorFromString(@"ObjcMsgSendWithString:withNum:withArray:");
((void (*) (id, SEL, NSString *, NSNumber *, NSArray *)) objc_msgSend) (self, sel, str, num, arr);
- (void)ObjcMsgSendWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
NSLog(@"%@, %@, %@", string, number, array[0]);
}
參數(shù)中有結(jié)構(gòu)體的處理方式
typedef struct ParameterStruct{
int a;
int b;
}MyStruct;
NSString *str = @"字符串 把結(jié)構(gòu)體轉(zhuǎn)換為對象";
NSNumber *num = @20;
NSArray *arr = @[@"數(shù)組值1", @"數(shù)組值2"];
MyStruct mystruct = {10,20};
NSValue *value = [NSValue valueWithBytes:&mystruct objCType:@encode(MyStruct)];
SEL sel = NSSelectorFromString(@"NSInvocationWithString:withNum:withArray:withValue:");
NSArray *objs = [NSArray arrayWithObjects:str, num, arr, value,nil];
[self performSelector:sel withObjects:objs];
- (void)NSInvocationWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array withValue:(NSValue *)value{
MyStruct struceBack;
[value getValue:&struceBack];
NSLog(@"%@, %@, %@, %d", string, number, array[0],struceBack.a);
}
第二點
/**
在主線程上執(zhí)行指定的方法饿敲,使用默認的模式(NSDefaultRunLoopMode)
默認的模式指:主線程中的方法進行排隊,是一個循環(huán)隊列逛绵,并且循環(huán)執(zhí)行怀各。
這個函數(shù)表示在主線程上執(zhí)行方法,YES表示需要阻塞主線程术浪,直到主線程將我們的代碼塊執(zhí)行完畢瓢对。
@param aSelector 要在主線程執(zhí)行的方法,該方法不能有返回值胰苏,并且只能有一個參數(shù)硕蛹。
@param arg 要傳遞的參數(shù),如果無參數(shù)硕并,就設(shè)為nil法焰。
@param wait 要執(zhí)行的aSelector方法,是否馬上執(zhí)行倔毙。
如果設(shè)置為YES:等待當前線程執(zhí)行完以后壶栋,主線程才會執(zhí)行aSelector方法;
設(shè)置為NO:不等待當前線程執(zhí)行完普监,就在主線程上執(zhí)行aSelector方法贵试。
如果,當前線程就是主線程凯正,那么aSelector方法會馬上執(zhí)行毙玻。
該方法用途:因為iPhone編程,對UI的修改廊散,只能在主線程上執(zhí)行桑滩。可以用該方法來完成UI的修改允睹。
*/
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait {
NSLog(@"用的多");
}
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array {
}
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait {
}
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array {
}
/**
在當前線程中執(zhí)行指定的方法运准,使用默認模式幌氮,并指定延遲。
@param aSelector 指定的方法胁澳。含義同上该互,不在贅述。
@param anArgument 同上
@param delay 指定延遲時間(秒)韭畸。
*/
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay {
NSLog(@"用的多");
}
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes {
}
這里需要補充的知識:
Runloop 相當于 win32 里面的消息循環(huán)機制宇智,它可以讓你根據(jù)事件/消息(鼠標消息,鍵盤消息胰丁,觸摸事件随橘,計時器消息等)來調(diào)度線程。
比如:在觸摸 UIView 時之所以能夠激發(fā) touchesBegan/touchesMoved 等等函數(shù)被調(diào)用锦庸。系統(tǒng)會自動為應(yīng)用程序的主線程生成一個與之對應(yīng)的 run loop 來處理其消息循環(huán)机蔗。讓調(diào)用更加簡單。也避免了繁瑣甘萧,復(fù)雜的操作蜒车。
一句話:Runloop是一種消息處理機制!
參考鏈接:
http://www.reibang.com/p/672c0d4f435a
http://blog.sina.com.cn/s/blog_7b9d64af0101bjw4.html
http://blog.csdn.net/meegomeego/article/details/20041887