iOS - Runtime 的常用方法

基本參數(shù)類型

SEL:類成員方法的指針,但不同于C語言中的函數(shù)指針绷杜,函數(shù)指針直接保存了方法的地址直秆,但SEL只是方法編號。
IMP:一個函數(shù)指針,保存了方法的地址
Method:方法的結(jié)構(gòu)體鞭盟,其中保存了方法的名字圾结,實現(xiàn)和類型描述字符串

1. 獲取所有屬性

只能獲取由 @property 聲明的屬性,不管是不是私有屬性,獲取的值是不帶下劃線的.

+ (NSArray *)getAllProperties{
    u_int count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    NSMutableArray *propertiesArrayM = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count; i++) {
        //此刻得到的propertyName為c語言的字符串
        objc_property_t property = properties[i];
        const char* propertyName = property_getName(property);
         //此步驟把c語言的字符串轉(zhuǎn)換為OC的NSString
        [propertiesArrayM addObject: [NSString stringWithUTF8String: propertyName]];
    }
    //class_copyPropertyList底層為C語言,所以我們一定要記得釋放properties
    free(properties);
    return propertiesArrayM;
}
2.獲取所有成員變量

獲取所有帶下劃線的成員變量,獲取的值帶下劃線.

+ (NSArray *)getAllMemberVariables{
    u_int count; //成員變量個數(shù)
    Ivar *vars = class_copyIvarList([self class], &count);
    NSMutableArray *memberVariablesArrayM = [NSMutableArray arrayWithCapacity:count];
    NSString *name=nil;
//    NSString *type=nil;
    for(int i = 0; i < count; i++) {
       Ivar thisIvar = vars[i];
       const char* ivarName = ivar_getName(thisIvar);
       name = [NSString stringWithUTF8String: ivarName];  //獲取成員變量的名字
       [memberVariablesArrayM addObject:key];
//        type = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisIvar)];  //獲取成員變量的類型
    }
    free(vars);
    return memberVariablesArrayM;
}
3.獲取方法名稱

只能獲取到 已經(jīng)實現(xiàn)的實例對象 方法名稱,包括Category中.只聲明未實現(xiàn)的該方法查詢不出來.注意: 因為@property 聲明的屬性是默認(rèn)實現(xiàn)了getset 方法的, 所以便利出來的方法中包含.

+ (NSArray *)getAllMethods{
    u_int count;
    Method *methodList = class_copyMethodList([self class], &count);
    NSMutableArray *methodArrayM = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count; i++) {
        Method temp = methodList[i];
        SEL name_f = method_getName(temp); // 方法的編號
         // 方法名
        const char* name_s = sel_getName(name_f);
        [methodArrayM addObject:[NSString stringWithUTF8String:name_s]];
    }
    free(methodList);
    return methodArrayM;
}

補充:
method相關(guān)函數(shù)

獲取具體實現(xiàn)的指針 返回值類型為 IMP
method_getImplementation(Method m)

獲取參數(shù)個數(shù)(因為每一個方法會默認(rèn)隱藏兩個參數(shù)齿诉,self筝野、_cmd晌姚,self代表方法調(diào)用者,_cmd代表這個方法的SEL) 所以參數(shù)的個數(shù)都是2起步
返回值類型為 int
method_getNumberOfArguments(Method m)

獲取返回值類型 返回值為字符串 例: 'B','v,'@','d'......等等 返回值的具體含義詳見 官網(wǎng)
返回值類型為 char *
method_copyReturnType(Method m)

獲取參數(shù)類型 按腳標(biāo)獲取 注意,有兩個默認(rèn)參數(shù),自定義參數(shù)從第三個開始.
返回值類型為char *
method_copyArgumentType(Method m, unsigned int index)

獲取 class_copyProtocolList:
拷貝協(xié)議列表歇竟。返回的一個Ivar列表的指針挥唠。獲取Ivar需要遍歷這個列表。
注意:調(diào)用copy的函數(shù)需要釋放資源free();
此函數(shù)不能獲取分類中添加的協(xié)議焕议。
此函數(shù)可以獲取動態(tài)添加的協(xié)議宝磨。

4.獲取當(dāng)前對象的屬性和屬性值

該方法只能獲取到 @property 聲明的屬性和屬性值
如果需要獲取成員變量及值的話 參照 2 需要注意的是上述方法獲取到是帶下劃線的成員變量名稱,所以通過kvc獲取對應(yīng)的value時 需要注意把下劃線去掉.

- (NSDictionary *)getAllPropertiesAndVaules{
    NSMutableDictionary *propsDic = [NSMutableDictionary dictionary];
    u_int outCount;
    objc_property_t *properties =class_copyPropertyList([self class], &outCount);
    for ( int i = 0; i<outCount; i++)
    {
        objc_property_t property = properties[i];
        const char* char_f =property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:char_f]; // 屬性名稱
        id propertyValue = [self valueForKey:propertyName]; //屬性值
        [propsDic setObject:propertyValue?:@"nil" forKey:propertyName];
    }
    free(properties);
    return propsDic;
}
5. 關(guān)聯(lián)對象

runtime的關(guān)聯(lián)可以讓我們動態(tài)的添加屬性,也就是 Category 添加屬性

//1. 聲明屬性
@property (nonatomic,assign) BOOL testProperty;

//2. 定義一個常量key 必須是C語言字符串
static char *TestPropertyKey = "TestPropertyKey";

//3. 實現(xiàn) set 方法
- (void)setTestProperty:(BOOL)testProperty{
    objc_setAssociatedObject(self, TestPropertyKey, [NSNumber numberWithBool:NO], OBJC_ASSOCIATION_ASSIGN);
}

//4. 實現(xiàn) get 方法
- (BOOL)testProperty{
    return [objc_getAssociatedObject(self, TestPropertyKey) boolValue];
}

補充:
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
參數(shù):
id object: 表示關(guān)聯(lián)者,是一個對象号坡,也就是給誰添加屬性值
const void *key: 獲取被關(guān)聯(lián)者的索引key
id value : 被關(guān)聯(lián)者懊烤,也就是屬性值 id類型.上面是一個BOOL值 所以轉(zhuǎn)換成NSNumber
objc_AssociationPolicy policy : 關(guān)聯(lián)時采用的協(xié)議
關(guān)聯(lián)協(xié)議類型:
OBJC_ASSOCIATION_ASSIGN 等價于 @property(assign)梯醒。
OBJC_ASSOCIATION_RETAIN_NONATOMIC 等價于 @property(nonatomic, strong)宽堆。
OBJC_ASSOCIATION_COPY_NONATOMIC 等價于@property(nonatomic, copy)。
OBJC_ASSOCIATION_RETAIN 等價于@property(atomic, strong)茸习。
OBJC_ASSOCIATION_COPY 等價于@property(atomic, copy)畜隶。

objc_getAssociatedObject(id object, const void * key)
參數(shù):
id object: 關(guān)聯(lián)者,是一個對象
const void *key: 獲取被關(guān)聯(lián)者的索引key
存進(jìn)去的是什么值,獲取的就是什么值.

移除關(guān)聯(lián)對象
該方法將會移除關(guān)聯(lián)對象中所有的被關(guān)聯(lián)者. 參數(shù)為關(guān)聯(lián)對象.
objc_removeAssociatedObjects(id _Nonnull object)
所以如果需要移除單個被關(guān)聯(lián)者 那么只需要使用 objc_setAssociatedObjectvalue 設(shè)為 nil 就OK了.

6.交換方法 method swizzling (俗稱:黑魔法)

Method Swizzling原理
Method Swizzing是發(fā)生在運行時的号胚,主要用于在運行時將兩個Method進(jìn)行交換籽慢,我們可以將Method Swizzling代碼寫到任何地方,但是只有在這段Method Swilzzling代碼執(zhí)行完畢之后互換才起作用猫胁。

正常

交換后

獲取類方法 class_getClassMethod
Method classMethod = class_getClassMethod([self class], @selector(testClassAction));
獲取實例方法 class_getInstanceMethod
Method instanceMethod = class_getInstanceMethod([self class], @selector(testObjcAction));
Method Swizzling 方法攪拌
method_exchangeImplementations(Method m1, Method m2)

實現(xiàn)

Method m1 = class_getInstanceMethod([self class], @selector(testObjcAction1));
Method m2 = class_getInstanceMethod([self class], @selector(testObjcAction2));
method_exchangeImplementations(m1, m2);

- (void)testObjcAction1{
    NSLog(@"testObjcAction1");
}
- (void)testObjcAction2{
    NSLog(@"testObjcAction2");
}
效果

補充:
使用 method_exchangeImplementations 交換方法實現(xiàn)時箱亿,只要 Method 參數(shù)不為 nil 那么都能替換.如果 Method 參數(shù)有一個為nil 那么就會交換.
其中 類方法與實例方法可以交換,正常執(zhí)行不會報錯和崩潰, 但是如果交換的方法中 使用到了類或者實例對象獨有的方法,那么就會崩潰. 因為雖然交換了方法的實現(xiàn), 但本質(zhì)執(zhí)行這個的對象沒有沒有變. 類還是類,對象還是對象.
參數(shù)不統(tǒng)一 編譯時不會報錯弃秆,但是運行時會報錯届惋。因為運行時最后找到的方法實現(xiàn)與調(diào)用的類型不一致 會造成崩潰.
什么時候需要交換方法了 就什么時候執(zhí)行代碼. 如果是在 Category 中替換方法,且是一開始就要替換的,那么建議在 + (void)load 中替換,因為這個方法在你導(dǎo)入當(dāng)前文件時就會執(zhí)行.建議加上dispatch_once 來保證替換代碼只會執(zhí)行一次.
替換方法前,確定方法已經(jīng)實現(xiàn).防止調(diào)用時崩潰

后續(xù)會持續(xù)更新.上述內(nèi)容都是壯骨自己理解的,如果有不對的地方,歡迎大家在評論交流下,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市菠赚,隨后出現(xiàn)的幾起案子脑豹,更是在濱河造成了極大的恐慌,老刑警劉巖衡查,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘩欺,死亡現(xiàn)場離奇詭異,居然都是意外死亡拌牲,警方通過查閱死者的電腦和手機(jī)俱饿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塌忽,“玉大人拍埠,你說我怎么就攤上這事⊙馄牛” “怎么了械拍?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵突勇,是天一觀的道長。 經(jīng)常有香客問我坷虑,道長甲馋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任迄损,我火速辦了婚禮定躏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芹敌。我一直安慰自己痊远,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布氏捞。 她就那樣靜靜地躺著碧聪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪液茎。 梳的紋絲不亂的頭發(fā)上逞姿,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音捆等,去河邊找鬼滞造。 笑死,一個胖子當(dāng)著我的面吹牛栋烤,可吹牛的內(nèi)容都是我干的谒养。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼明郭,長吁一口氣:“原來是場噩夢啊……” “哼买窟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起达址,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蔑祟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沉唠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疆虚,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年满葛,在試婚紗的時候發(fā)現(xiàn)自己被綠了径簿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘀韧,死狀恐怖篇亭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锄贷,我是刑警寧澤译蒂,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布曼月,位于F島的核電站,受9級特大地震影響柔昼,放射性物質(zhì)發(fā)生泄漏哑芹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一捕透、第九天 我趴在偏房一處隱蔽的房頂上張望聪姿。 院中可真熱鬧,春花似錦乙嘀、人聲如沸末购。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盟榴。三九已至,卻和暖如春嘉冒,著一層夾襖步出監(jiān)牢的瞬間曹货,已是汗流浹背咆繁。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工讳推, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玩般。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓银觅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坏为。 傳聞我的和親對象是個殘疾皇子究驴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345