iOS用runtime給一個類動態新增方法 ---class_addMethod
先介紹下class_addMethod這個fangfa
- /**
- * Adds a new method to a class with a given name and implementation.
- *
- * @param cls The class to which to add a method.
- * @param name A selector that specifies the name of the method being added.
-
* @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
- * @param types An array of characters that describe the types of the arguments to the method.
- *
- * @return YES if the method was added successfully, otherwise NO
- * (for example, the class already contains a method implementation with that name).
- *
-
* @note class_addMethod will add an override of a superclass's implementation,
- * but will not replace an existing implementation in this class.
- * To change an existing implementation, use method_setImplementation.
- */
- OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
- const char *types)
- OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
這是文件
描述就是:給一個類新增一個新的方法和該方法的具體實現
引數:
第一個
Class cls
cls :你要新增新方法的那個類
第二個
SEL name
name :要新增的方法
第三個
IMP imp
imp:指向實現方法的指標 就是要新增的方法的實現部分
第四個
const char *types
*types:我們要新增的方法的返回值和引數 叫 type encodings 這個東西怎麼寫我就不說了 可以去查 在這裡我有一個懶辦法得到它不用暴力手打 往後看
然後這個方法的描述和引數說完了,下面說說他用在哪,
給一個類用執行時新增一個方法 他可以用來解決我們開發中的這個問題------“unrecognized selector sent to instance 0x7faa2a132c0”
在OC中我們呼叫一個方法時 有個關鍵字 叫 “訊息機制” 就是執行 [receiver message]; 這句程式碼其實就是傳送了一個訊息 (發訊息的詳細過程我就不寫了 感興趣 去查),
訊息裡面帶著你呼叫這個方法的名稱和引數以及是哪個物件傳送的訊息,然後 發出訊息後 如果有對應的方法響應,就執行方法,沒有的話就報“unrecognized selector sent to instance 0x7faa2a132c0” ,然後就crash ,為了防止crash我們可以用class_addMethod給找不到對應方法即將crash的訊息新增一個與之對應的方法來防止其因為找不到相應方法而crash
在OC中找不到對相應的實現方法時 有補救機制 即 會先呼叫動態決議方法 該方法解決不了問題 再呼叫重定向方法 都解決不了問題是 crash
動態決議方法:
- + (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- + (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
重定向方法:
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
下面上程式碼:給一個類的物件呼叫一個未實現的方法 然後用runtime 在動態決議方法中為其新增實現 防止crash
Person類
- @implementation Person
- + (BOOL)resolveInstanceMethod:(SEL)sel {
- Method exchangeM = class_getInstanceMethod([self class], @selector(eatWithPersonName:));
- class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(eatWithPersonName:)),method_getTypeEncoding(exchangeM));
- return YES;
- }
- - (void)eatWithPersonName:(NSString *)name {
- NSLog(@"Person %@ start eat ",name);
- }
- @end
裡面重寫resolveInstanceMethod方法 給找不到實現的方法 新增實現 - (void)eatWithPersonName:(NSString *)name;
第一句
Method exchangeM = class_getInstanceMethod([self class], @selector(eatWithPersonName:));
是為下面method_getTypeEncoding(exchangeM) 做鋪墊 這樣就拿到了eatWithPersonName方法的type encodings 用作class_addMethod的第四個引數,就是上面我提到的不用暴力手打的獲取type encodings的方法
然後呼叫class_addMethod 為當前類[self class] 的sel 方法 新增實現 class_getMethodImplementation(self, @selector(eatWithPersonName:)) 這一串就是拿到方法eatWithPersonName的IMP指標 然後最後一個引數是 type encodings
然後測試
- Person *person = [[Person alloc]init];
- //呼叫Person未實現方法eat
- [person performSelector:@selector(eat) withObject:@"minzhe"];
執行結果:
2017-07-14 14:41:35.297 RunTimeAddMethod[1458:379458] Person minzhe start eat
好了 新增上了 這樣就沒有 crash