iOS-動態添加方法

動態添加方法可以在動態創建的類中添加也可以在已存在的類中添加,先來看下動態添加方法的定義函數:

OBJC_EXPORT BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

函數分析:

參數1:Class 要向其添加方法的類;
參數2:SEL 指定要添加的方法的名稱;
參數3:IMP 一個新方法的實現函數,函數至少有兩個參數id selfSEL _cmd
參數4:types 描述方法參數類型的字符數組,參考

返回值: 方法添加成功,則返回 YES,否則返回 NO(例如:該類已包含具有該名稱的方法實現)。

我們以已經存在的類中添加方法爲例,先創建一個Person類:

@interface Person : NSObject

@end

先爲Person類添加一個實例對象方法(減號方法):

Person *p = Person.new;
BOOL isAdd = class_addMethod(Person.class, @selector(eat:), (IMP)EatFunction, "v@:@");
[p performSelector:@selector(eat:) withObject:@"芒果"];

// 帶返回值寫法
// class_addMethod(Person.class, @selector(eat:), (IMP)EatFunction, "@@:@");
// NSString *food = [p performSelector:@selector(eat:) withObject:@"芒果"];

v@: 表示一個void類型的方法,無返回值,後面一個@表示一個對象類型的參數(例中NSString類型);由於Person類中沒有聲明eat方法,無法直接用p對象調用,所以通過performSelector執行我們動態添加的eat方法。

void EatFunction( id self, SEL _cmd, NSString *food) {
    NSLog(@"%@",food);
}
// 帶返回值寫法
//NSString * EatFunction( id self, SEL _cmd, NSString *food) {
//    NSLog(@"%@",food);
//    return food;
//}

先爲Person類添加一個類對象方法(加號方法):
之前我們說過,類對象方法是存儲在該類對應的元類中的,所以添加類方法需要向元類中添加,獲取元類的有兩中方法:
1、通過objc_getMetaClass()函數,傳入參數爲C格式的類名稱字符串,返回元類

OBJC_EXPORT Class _Nullable
objc_getMetaClass(const char * _Nonnull name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

2、通過object_getClass()函數,注意這個函數既可以獲取該類,也可以獲取該類對應的元類,就看你傳入的參數類型;當傳入參數爲Perosn類的實例對象即p時,返回的是該類;當傳入的參數爲Perosn類對象時,返回的就是Person類的元類

OBJC_EXPORT Class _Nullable
object_getClass(id _Nullable obj) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

注:雖然object_getClass()函數的參數是id(任意對象)類型,但是類也是對象,所以可以把類對象當作參數。

添加方法時稍微有些變化:

class_addMethod(object_getClass(Person.class), @selector(eat:), (IMP)EatFunction, "v@:@");
// 或者
// BOOL isAdd = class_addMethod(objc_getMetaClass(NSStringFromClass(Person.class).UTF8String), @selector(eat:), (IMP)EatFunction, "v@:@");

執行方需要用Person去調用

[Person performSelector:@selector(eat:) withObject:@"黃瓜"];

有人會疑問performSelector:不是實例對象方法嗎?爲什麼用類對象去調用也可以實現呢?還記得這張圖嗎?
在這裏插入圖片描述
元類中的superclass指針指向其父類對應的的元類,而根類(NSObject)的元類的superclass指針卻指向的是根類的實例類(上圖中間一排,普通類),所以當Person調用類方法時,如果Person類沒有該方法,就會一級一級向父類的元類中找,如果找到根類的元類還是沒有該類方法的實現,就向根類的普通類中去找相應的實例方法的實現,如果找到了也可以調用,如果還是沒有程序就會Crash。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章