iOS底層探索之類的結構(下)

類的結構探索分析.png

在之前的幾篇博客里面息罗,已經(jīng)介紹了掂咒,類的底層結構,還有isa的走位流程,元類的繼承鏈绍刮,對象方法温圆,類方法的存放位置,以及如何通過lldb獲取成員和屬性孩革、對象方法岁歉,類方法。

iOS底層探索之類的結構(上)

iOS底層探索之類的結構(中)

本篇博客主要做一些補充和擴展膝蜈。

成員變量和屬性

開發(fā)年限比較久的iOS程序員锅移,都知道在iOS5之前,經(jīng)潮ゲ看到一個大括號里面定義了成員變量非剃,同時用了@property聲明,而且還在@implementation中使用@synthesize方法推沸。

其實备绽,發(fā)生這種狀況根本原因是蘋果將默認編譯器從GCC轉換為LLVM(low level virtual machine),才不再需要為屬性聲明實例變量了鬓催。

在沒有更改之前疯坤,屬性的正常寫法需要成員變量+ @property + @synthesize成員變量三個步驟。 更換為LLVM之后深浮,編譯器在編譯過程中發(fā)現(xiàn)沒有新的實例變量后,就會生成一個下劃線開頭的實例變量眠冈。因此現(xiàn)在我們不必在聲明一個實例變量飞苇。

現(xiàn)在@property聲明的屬性不僅僅默認給我們生成一個_類型的成員變量,同時也會生成setter/getter方法蜗顽。

類信息

clang源碼

  • 屬性 = 帶下劃線成員變量 + setter + getter ?法
  • 實例變量 : 特殊的成員變量 (類的實例化)

從以上clang底層源碼也可以驗證布卡,@property聲明的屬性默認會生成一個_類型的成員變量,同時也會生成setter/getter方法雇盖。

objc_setProperty

但是我們從上面的底層源碼發(fā)現(xiàn)了一個問題忿等,有的set方法里面是通過objc_setProperty獲取屬性,有的是通過內(nèi)存的平移崔挖。這是什么原因了呢贸街?通過對比不同屬性設置帶有copy和不帶有copy的底層源碼分析,可以找到原因是copy這個設置的問題狸相!那么為驗證這個結論薛匪,我們?nèi)サ讓涌纯矗?/p>

LLVM

那我們?nèi)?code>llvm源碼里面看看,objc_setProperty的實現(xiàn)邏輯脓鹃,你怎么就知道去llvm不是去objc源碼看呢逸尖?請看下圖分析

在這里插入圖片描述

首先在底層也就是下層,蘋果的源碼是固定的,是不會改變的(這里的不變是指不會根據(jù)你代碼來改變娇跟,因為底層邏輯已經(jīng)寫好了)岩齿。在上層,我們程序開發(fā)人員苞俘,會寫很多中屬性盹沈,那么就會有各種各種的奇奇怪怪的代碼出現(xiàn),就有各種的set形式的代碼苗胀,比如setName襟诸、setAge、setXX基协。歌亲。。等等澜驮。

這對編譯器來說陷揪,只是_cmd的方法名字不一樣,那就需要一個中間層來把這些奇奇怪怪的杂穷,做一下處理悍缠,以便于和底層去交互,因為底層邏輯已經(jīng)封裝好耐量,不可能動態(tài)的改變的飞蚓。那我們?nèi)パ芯?code>objc_setProperty就不可能在運行時了,只能在編譯時了廊蜒,所以我們?nèi)ゾ?code>llvm里面看看趴拧!

那上層和objc_setProperty之間有著怎樣的聯(lián)系呢?

getGetPropertyFn

我們在llvm里面找到了getGetPropertyFn這個函數(shù)山叮,是獲取創(chuàng)建的CreateRuntimeFunction著榴,里面有objc_setProperty這個參數(shù)。那么為什么創(chuàng)建呢屁倔?繼續(xù)往下看看脑又,在哪里調用了
getGetPropertyFn()

既然調用了,肯定是有條件的锐借,是真的是是因為propertycopy嗎问麸?我們找到條件,就可以反推知道objc_setProperty的作用了瞎饲。繼續(xù)往下看
PropertyImplStrategy

看看上面的代碼有沒有發(fā)現(xiàn)什么口叙?這有個屬性imp的策略PropertyImplStrategy,那么再繼續(xù)往下找
PropertyImplStrategy

看到?jīng)]有嗅战,如果IsCopy為真就Kind = GetSetProperty妄田,這也就驗證了博客開篇的猜想俺亮,只有屬性里面用copy修飾的,才會有objc_setProperty疟呐。

代碼獲取類的信息

iOS底層探索之類的結構(上)
iOS底層探索之類的結構(中)
在之前的博客里面脚曾,我們已經(jīng)知道了,可以在控制臺通過lldb獲取類的信息启具,那么現(xiàn)在我們通過代碼來獲取下本讥。

class_copyMethodList

void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //獲取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}

class_getInstanceMethod

//獲取實例方法
void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

class_getClassMethod

//獲取類方法
void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

//    - (void)sayHello;
//    + (void)sayHappy;
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

class_getMethodImplementation

void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));// 0
    // sel -> imp 方法的查找流程 imp_farw
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);

}

代碼測試驗證


代碼測試驗證

IMP,SEL

  • SEL : 類成員方法的指針,但不同于C語言中的函數(shù)指針鲁冯,函數(shù)指針直接保存了方法的地址拷沸,但SEL只是方法編號。
  • IMP:一個函數(shù)指針,保存了方法的地址
  • SEL和IMP的關系就:SEL就相當于書本的?錄標題薯演,IMP就是書本的?碼撞芍,函數(shù)就是具體頁碼對應的內(nèi)容。

擴展

我們已經(jīng)學了這么多類的相關的知識了跨扮,那么我們看看下面這個面試題

    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
    BOOL re3 = [(id)[JPPerson class] isKindOfClass:[JPPerson class]];    
    BOOL re4 = [(id)[JPPerson class] isMemberOfClass:[JPPerson class]];    
 
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n,%hhd,%hhd",re1,re2,re3,re4,re9,re10);

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     
    BOOL re7 = [(id)[JPPerson alloc] isKindOfClass:[JPPerson class]];       
    BOOL re8 = [(id)[JPPerson alloc] isMemberOfClass:[JPPerson class]];     
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

代碼運行結果


代碼運行結果

+ isKindOfClass

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- isKindOfClass

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

+ isMemberOfClass

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- isMemberOfClass

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
- (Class)class {
    return object_getClass(self);
}

分析

  • re1 傳入的是NSObject的類序无,獲取元類NSObject不等,繼續(xù)尋找獲取元類的父類為NSObject與傳入的值相等衡创,返回true
  • re2是NSObject類調用類方法isMemberOfClassNSObject類比較帝嗡,很明顯,NSObject的元類NSObject本身并不相等璃氢,所以返回false
  • re3 傳入的是JPPerson的類哟玷,獲取元類JPPerson不等,繼續(xù)尋找獲取元類的父類為NSObject的元類一也,與傳入的值依舊不等碗降,繼續(xù)往上NSObject元類的父類為NSObject依舊不等,再往上就是nil塘秦,最后返回false
    -re4JPPerson類調用類方法isMemberOfClassJPPerson類比較,JPPerson的元類JPPerson本身并不相等动看,所以返回false.
  • re5 傳入的是NSObject的實例尊剔,獲取對象的類,與NSObject相等菱皆,返回true
  • re7 傳入的是JPPerson的實例须误,獲取對象的類,與JPPerson相等仇轻,返回true
    -re8JPPerson的實例調用實例方法isMemberOfClassJPPerson類比較京痢,明顯的他們是相同的,所以返回true

更多內(nèi)容持續(xù)更新

?? 請動動你的小手篷店,點個贊????

?? 喜歡的可以來一波祭椰,收藏+關注臭家,評論 + 轉發(fā),以免你下次找不到我方淤,哈哈????

??歡迎大家留言交流钉赁,批評指正,互相學習??携茂,提升自我??

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末你踩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子讳苦,更是在濱河造成了極大的恐慌带膜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸳谜,死亡現(xiàn)場離奇詭異膝藕,居然都是意外死亡,警方通過查閱死者的電腦和手機卿堂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門束莫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人草描,你說我怎么就攤上這事览绿。” “怎么了穗慕?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵饿敲,是天一觀的道長。 經(jīng)常有香客問我逛绵,道長怀各,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任术浪,我火速辦了婚禮瓢对,結果婚禮上,老公的妹妹穿的比我還像新娘胰苏。我一直安慰自己硕蛹,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布硕并。 她就那樣靜靜地躺著法焰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪倔毙。 梳的紋絲不亂的頭發(fā)上埃仪,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音陕赃,去河邊找鬼卵蛉。 笑死颁股,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的毙玻。 我是一名探鬼主播豌蟋,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桑滩!你這毒婦竟也來了梧疲?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤运准,失蹤者是張志新(化名)和其女友劉穎幌氮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胁澳,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡该互,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了韭畸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宇智。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胰丁,靈堂內(nèi)的尸體忽然破棺而出随橘,到底是詐尸還是另有隱情,我是刑警寧澤锦庸,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布机蔗,位于F島的核電站,受9級特大地震影響甘萧,放射性物質發(fā)生泄漏萝嘁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一扬卷、第九天 我趴在偏房一處隱蔽的房頂上張望牙言。 院中可真熱鬧,春花似錦怪得、人聲如沸嬉挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拔恰,卻和暖如春因谎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颜懊。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工财岔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留风皿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓匠璧,卻偏偏與公主長得像桐款,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子夷恍,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容