前言:方法替換,可以替換任意外部類的方法,而動態添加方法只能實現在被添加類創建的對象里,但是將方法替換和動態添加方法結合使用,可以實現,對任意外部類動態添加需要的方法,這個方法可以是類方法也可以是實例方法,這個外部類也可以是沒有任何方法聲明和實現的類。主要思路:使用運行時的方法替換將在外部類將自定義...
前言:
方法替換,可以替換任意外部類的方法,而動態添加方法只能實現在被添加類創建的對象里,但是將方法替換和動態添加方法結合使用,可以實現,對任意外部類動態添加需要的方法,這個方法可以是類方法也可以是實例方法,這個外部類也可以是沒有任何方法聲明和實現的類。
主要思路:
使用運行時的方法替換將在外部類將自定義方法hy_resolveInstanceMethod
或hy_resolveClassMethod
(用hy_
首碼表示是我自定義的方法)和需要被添加的類中的resolveInstanceMethod
或者resolveClassMethod
方法替換,替換之前在hy_resolveInstanceMethod
或hy_resolveClassMethod
方法內部寫好本應該在resolveInstanceMethod
或者resolveClassMethod
方法內部寫好的runtime動態添加方法的邏輯。
可能有點繞,不過至少需要繼續閱讀源碼,思考其中的邏輯,其實不難,前提是熟悉使用runtime的方法。
缺陷:1、含參數的方法難以處理,參數值需要根據實際業務邏輯而定。
Before use import <objc/message.h>
,need following:
Create Person.h and Person.m
Person.h:
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 5 @endPerson.m:
1 #import "Person.h" 2 3 @implementation Person 4 5 @end
Create OtherPerson.h and OtherPerson.m
OtherPerson.h:
1 #import <Foundation/Foundation.h> 2 3 @interface OtherPerson : NSObject 4 5 6 @end
OtherPerson.m:
1 // 2 // Created by HEYANG on 16/1/11. 3 // Copyright © 2016年 HEYANG. All rights reserved. 4 // 5 6 #import "OtherPerson.h" 7 #import <objc/message.h> 8 9 @implementation OtherPerson 10 11 12 +(void)load{ 13 Class clazz = NSClassFromString(@"Person"); 14 15 //獲取替換前的類方法 16 Method instance_eat = 17 class_getClassMethod(clazz, @selector(resolveInstanceMethod:)); 18 //獲取替換後的類方法 19 Method instance_notEat = 20 class_getClassMethod(self, @selector(hy_resolveInstanceMethod:)); 21 22 //然後交換類方法 23 method_exchangeImplementations(instance_eat, instance_notEat); 24 25 //獲取替換前的類方法 26 Method class_eat = 27 class_getClassMethod(clazz, @selector(resolveClassMethod:)); 28 //獲取替換後的類方法 29 Method class_notEat = 30 class_getClassMethod(self, @selector(hy2_resolveClassMethod:)); 31 32 //然後交換類方法 33 method_exchangeImplementations(class_eat, class_notEat); 34 35 } 36 37 void eat_1(id self,SEL sel) 38 { 39 NSLog(@"到底吃不吃飯了"); 40 NSLog(@"%@ %@",self,NSStringFromSelector(sel)); 41 } 42 void eat_2(id self,SEL sel, NSString* str1,NSString* str2) 43 { 44 NSLog(@"到底吃不吃飯了"); 45 NSLog(@"%@ %@",self,NSStringFromSelector(sel)); 46 NSLog(@"列印兩個參數值:%@ and %@",str1,str2); 47 } 48 49 50 +(BOOL)hy_resolveInstanceMethod:(SEL)sel{ 51 //當sel為實現方法中 有 eat 方法 52 if (sel == NSSelectorFromString(@"eat")) { 53 //就 動態添加eat方法 54 55 // 第一個參數:給哪個類添加方法 56 // 第二個參數:添加方法的方法編號 57 // 第三個參數:添加方法的函數實現(函數地址) 58 // 第四個參數:函數的類型,(返回值+參數類型) v:void @:對象->self :表示SEL->_cmd 59 class_addMethod(self, sel, (IMP)eat_1, "v@:"); 60 } 61 return YES; 62 } 63 +(BOOL)hy2_resolveClassMethod:(SEL)sel{ 64 65 if (sel == NSSelectorFromString(@"eat:with:")) { 66 67 class_addMethod(objc_getMetaClass("Person"), sel, (IMP)eat_2, "v#:@@"); 68 } 69 70 return YES; 71 } 72 73 @end
last In file ‘main.m’:
main.m:
1 /** 2 * 3 * Swap Method and Dynamic add Method (交換方法和動態添加方法) 4 * 5 */ 6 7 #import <Foundation/Foundation.h> 8 9 //ignore undeclared warm 忽視未聲明的警告 10 #pragma clang diagnostic push 11 #pragma clang diagnostic ignored "-Wundeclared-selector" 12 13 int main(int argc, const char * argv[]) { 14 @autoreleasepool { 15 //get this Person class 拿到了這個Person類 16 Class clazz = NSClassFromString(@"Person"); 17 //get this Person Instance 拿到這個Person實例 18 id person = [[clazz alloc] init]; 19 20 //send message to 'eat' method in Person Class or Person Instance 21 //發送消息給Person類或者Person實例的‘eat’方法 不含參數 22 [person performSelector:@selector(eat) withObject:nil]; 23 //發送消息給Person類的‘eat’方法 含兩個參數 24 [clazz performSelector:@selector(eat:with:) 25 withObject:@"Hello" 26 withObject:@"World"]; 27 } 28 return 0; 29 } 30 31 #pragma clang diagnostic pop