OC底層原理08-方法、屬性查剖、成員變量的歸屬

一钾虐、準(zhǔn)備工作

  • 在源碼中創(chuàng)建類(lèi)GomuPerson
GomuPerson.h
@interface GomuPerson : NSObject
{
    //: 成員變量
    NSString *hobby;
    //: 特殊的成員變量效扫,實(shí)例變量,能夠被實(shí)例的對(duì)象的成員變量叫實(shí)例變量
    NSObject *obj;
}
//: 創(chuàng)建屬性
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *sex;

//: 創(chuàng)建實(shí)例方法直砂、類(lèi)方法
- (void)sayNO;
+ (void)sayLove;

GomuPerson.m
//: 實(shí)現(xiàn)方法
- (void)sayNO{}
+ (void)sayLove{}

@end

二菌仁、通過(guò)runtime的api拿方法、屬性静暂、成員變量

2.1 通過(guò)gomu_copyMethodListGomuPerson里面的方法

//: 打印類(lèi)中的所有方法
void gomu_copyMethodList(Class class){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Method const method = methods[i];
        //: 獲取方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        GOMULog(@"Method, name: %@",methodName);
    }
    free(methods);
}

//: 調(diào)用方法
GomuPerson *p = [GomuPerson alloc];
//: 獲取實(shí)例方法
gomu_copyMethodList(object_getClass(p));
//: 打印
Method, name: sayNO
Method, name: sex
Method, name: setSex:
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:

//: 獲取類(lèi)方法
gomu_copyMethodList(object_getClass([GomuPerson class]));
//: 打印
Method, name: sayLove

結(jié)論:

  • 對(duì)象方法存在類(lèi)
  • 系統(tǒng)在編譯中會(huì)自動(dòng)生成屬性的get济丘、set方法
  • 系統(tǒng)在編譯中也會(huì)生成c++.cxx_destruct方法
  • 類(lèi)方法存在元類(lèi)

2.2 通過(guò)gomu_copyPropertyList拿GomuPerson里面的屬性

#pragma mark -- 獲取屬性
void gomu_copyPropertyList(Class class){
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        //: 獲取屬性
        const char *propertyName = property_getName(property);
        GOMULog(@"Property, name: %s",propertyName);
    }
    free(propertys);
}

//: 調(diào)用方法
GomuPerson *p = [GomuPerson alloc];
//: 獲取屬性
gomu_copyPropertyList(object_getClass(p));
//: 打印
Property, name: name
Property, name: sex

結(jié)論:

  • 屬性存在PropertyList
  • 成員變量不存在PropertyList

2.3 通過(guò)gomu_copyIvarList拿GomuPerson里面的成員變量

#pragma mark -- 獲取成員變量
void gomu_copyIvarList(Class class){
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar const ivar = ivars[i];
        //: 獲取成員變量
        const char *ivarName = ivar_getName(ivar);
        GOMULog(@"Ivar, name: %s",ivarName);
    }
    free(ivars);
}
//: 調(diào)用方法
GomuPerson *p = [GomuPerson alloc];
//: 獲取屬性
gomu_copyIvarList(object_getClass(p));
//: 打印
Ivar, name: hobby
Ivar, name: obj
Ivar, name: _name
Ivar, name: _sex

結(jié)論

  • 成員變量存在IvarList
  • 系統(tǒng)編譯中會(huì)自動(dòng)給屬性生成帶_成員變量

2.4 通過(guò)class_getInstanceMethod判斷對(duì)象方法和類(lèi)方法的歸屬

void gomuInstanceMethod_classToMetaclass(Class pClass){
    //: 獲取元類(lèi)
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    //: 判斷類(lèi)中是否有對(duì)象方法`sayNO`:有
    Method method1 = class_getInstanceMethod(pClass, @selector(sayNO));
    //: 判斷元類(lèi)中是否有對(duì)象方法`sayNO`:沒(méi)有
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayNO));
        //: 判斷類(lèi)中是否有類(lèi)方法`sayLove`的對(duì)象方法:沒(méi)有
    Method method3 = class_getInstanceMethod(pClass, @selector(sayLove));
    //: 判斷類(lèi)元中是否有類(lèi)方法`sayLove`的對(duì)象方法:有
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayLove));
    
    GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);
    
    GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);

//: 調(diào)用
gomuInstanceMethod_classToMetaclass(pClass);

//: 打印
gomuInstanceMethod_classToMetaclass : 0x1000031e0 - 0x0 - 0x0 - 0x100003178
}

結(jié)論:

  • 對(duì)象方法存在類(lèi)
  • 類(lèi)方法存在元類(lèi),并且以對(duì)象方法的形式存在元類(lèi)

2.5 通過(guò)class_getClassMethod判斷對(duì)象方法和類(lèi)方法的歸屬

void gomuClassMethod_classToMetaclass(Class pClass){
    
    //: 獲取元類(lèi)
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    //: 判斷類(lèi)中是否有對(duì)象方法`sayNO`的類(lèi)方法:沒(méi)有
    Method method1 = class_getClassMethod(pClass, @selector(sayNO));
    //: 判斷元類(lèi)中是否有對(duì)象方法`sayNO`的類(lèi)方法:沒(méi)有
    Method method2 = class_getClassMethod(metaClass, @selector(sayNO));
    //: 判斷類(lèi)中是否有類(lèi)方法`sayLove`:有
    Method method3 = class_getClassMethod(pClass, @selector(sayLove));
    //: 判斷元類(lèi)中是否有類(lèi)方法`sayLove`:有
    Method method4 = class_getClassMethod(metaClass, @selector(sayLove));
    
    GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);

//: 調(diào)用
gomuClassMethod_classToMetaclass(pClass);

//: 打印
gomuClassMethod_classToMetaclass : 0x0 - 0x0 - 0x100003180 - 0x100003180
}

結(jié)論:

  • 不管類(lèi)還是元類(lèi)中,都不會(huì)有對(duì)象方法類(lèi)方法
  • 類(lèi)元類(lèi)中摹迷,都有類(lèi)方法
2.5.1 元類(lèi)中為什么會(huì)有類(lèi)方法疟赊,不是說(shuō)類(lèi)方法在元類(lèi)是以對(duì)象方法的形成存在的嗎,進(jìn)入class_getClassMethod內(nèi)部查看源碼
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
    //: 當(dāng)cls是元類(lèi)峡碉,走到這里近哟,getMeta()返回元類(lèi)
    //: 所以 class_getClassMethod 傳入元類(lèi)相當(dāng)于 class_getInstanceMethod傳入元類(lèi)
    return class_getInstanceMethod(cls->getMeta(), sel);
}

//: 如果是元類(lèi),則直接返回自己
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

結(jié)論:

  • 當(dāng)class_getClassMethodcls傳入的是元類(lèi)异赫,則會(huì)調(diào)用class_getInstanceMethod椅挣,執(zhí)行到cls->getMeta()返回自己。
  • class_getClassMethod(metaClass, @selector(sayLove))等價(jià)于class_getInstanceMethod(metaClass, @selector(sayLove));
  • 類(lèi)方法元類(lèi)中還是以對(duì)象方法的方法存在

2.6 查看類(lèi)方法/對(duì)象方法在類(lèi)/元類(lèi)中的IMP

void gomuIMP_classToMetaclass(Class pClass){
    
    //: 獲取元類(lèi)
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    //: 在類(lèi)中獲取對(duì)象方法`sayNO`的IMP
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayNO));
    //: 在元類(lèi)中獲取對(duì)象方法`sayNO`的IMP
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayNO));
    //: 在類(lèi)中獲取類(lèi)方法`sayLove`的IMP
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayLove));
    //: 在元類(lèi)中獲取類(lèi)方法`sayLove`的IMP
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayLove));

    GOMULog(@"%p - %p - %p - %p",imp1,imp2,imp3,imp4);

//: 調(diào)用
gomuIMP_classToMetaclass(pClass);

//: 打印
0x100001b90 - 0x1002c4140 - 0x1002c4140 - 0x100001b80
}

結(jié)論:

  • 對(duì)象方法sayNOGomuPerson中找到了IMP
  • 對(duì)象方法sayNOGomuPerson的元類(lèi)中找到了IMP
  • 類(lèi)方法sayLoveGomuPerson的中找到了IMP
  • 類(lèi)方法sayLoveGomuPerson的元類(lèi)中找到了IMP

問(wèn)題:imp1,imp4都能理解塔拳,imp2,imp3怎么也有IMP鼠证?

2.6.1 第一種方式探索:查看源碼
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    //: 當(dāng)沒(méi)有找到IMP,會(huì)返回一個(gè)`_objc_msgForward`
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}
  • 從源碼不難看出靠抑,IMP是肯定存在的量九,只是有可能這個(gè)IMP可能是_objc_msgForward類(lèi)型
2.6.2 第一種方式探索:lldb調(diào)試
//: 下斷點(diǎn),分別打印
(lldb) p/x imp1
(IMP) $0 = 0x0000000100001b90 (GomuTest`-[GomuPerson sayNO])
(lldb) p/x imp2
(IMP) $1 = 0x00000001002c4140 (libobjc.A.dylib`_objc_msgForward)
(lldb) p/x imp3
(IMP) $2 = 0x00000001002c4140 (libobjc.A.dylib`_objc_msgForward)
(lldb) p/x imp4
(IMP) $3 = 0x0000000100001b80 (GomuTest`+[GomuPerson sayLove])

結(jié)論:

  • 對(duì)象方法的imp類(lèi)中可以拿到
  • 類(lèi)方法imp元類(lèi)中可以拿到
  • imp不會(huì)為空颂碧,為空會(huì)默認(rèn)_objc_msgForward

三荠列、拓展知識(shí)

  • imp : 函數(shù)指針地址
  • sel : 方法編號(hào)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市载城,隨后出現(xiàn)的幾起案子肌似,更是在濱河造成了極大的恐慌,老刑警劉巖诉瓦,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件川队,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡睬澡,警方通過(guò)查閱死者的電腦和手機(jī)固额,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)煞聪,“玉大人斗躏,你說(shuō)我怎么就攤上這事∥舾” “怎么了啄糙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)云稚。 經(jīng)常有香客問(wèn)我隧饼,道長(zhǎng),這世上最難降的妖魔是什么碱鳞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任桑李,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贵白。我一直安慰自己率拒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布禁荒。 她就那樣靜靜地躺著猬膨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呛伴。 梳的紋絲不亂的頭發(fā)上勃痴,一...
    開(kāi)封第一講書(shū)人閱讀 51,775評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音热康,去河邊找鬼沛申。 笑死,一個(gè)胖子當(dāng)著我的面吹牛姐军,可吹牛的內(nèi)容都是我干的铁材。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奕锌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼著觉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起惊暴,我...
    開(kāi)封第一講書(shū)人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤饼丘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后辽话,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體肄鸽,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年屡穗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贴捡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忽肛。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡村砂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屹逛,到底是詐尸還是另有隱情础废,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布罕模,位于F島的核電站评腺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏淑掌。R本人自食惡果不足惜蒿讥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芋绸,春花似錦媒殉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至马昙,卻和暖如春桃犬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背行楞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工攒暇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人子房。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓扯饶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親池颈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尾序,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356