神經(jīng)病院objc runtime入院考試

本文的題目源自2014年11月1日合溺,sunny分享的objc runtime浆洗。

在一次看到這四個題目的時候流炕,我居然很巧妙的避開了所有的正確答案,這讓我對自己的技術水平產(chǎn)生了深深的懷疑铅匹。更讓我感到絕望的是有些題目我居然看參考答案都無法理解Q荷蕖!包斑!時隔多年流礁,當我回過頭來繼續(xù)看這些題目的時候,我發(fā)現(xiàn)我居然能夠理解了罗丰,這種能夠看到自己進步的感覺神帅,真好。由于前三道題目比較簡單萌抵,而且太多的博客講解了找御,在此不做細談,重點來看第四題绍填。

(4) 下面的代碼會霎桅?Compile Error / Runtime Crash / NSLog…?

@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Sark
- (void)speak {
   NSLog(@"my name's %@", self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
   [super viewDidLoad];
   id cls = [Sark class];
   void *obj = &cls;
   [(__bridge id)obj speak];
}
@end

答案

(4)編譯運行正常,輸出ViewController中的self對象讨永。 編譯運行正常滔驶,調(diào)用了-speak方法,由于

id cls = [Sark class];
void *obj = &cls;

obj已經(jīng)滿足了構成一個objc對象的全部要求(首地址指向ClassObject)卿闹,遂能夠正常走消息機制揭糕;
由于這個人造的對象在棧上,而取self.name的操作本質(zhì)上是self指針在內(nèi)存向高位地址偏移(32位下一個指針是4字節(jié))锻霎,按viewDidLoad執(zhí)行時各個變量入棧順序從高到底為(self, _cmd, self.class, self, obj)(前兩個是方法隱含入?yún)⒅牵S后兩個為super調(diào)用的兩個壓棧參數(shù)),遂棧低地址的obj+4取到了self旋恼。

這道題考察的重點是:

1.什么是一個OC的對象吏口?
2.對象怎么去調(diào)用一個方法?
3.對象怎么去獲取一個屬性的值?

客官锨侯,坐下來喝杯茶,且聽我徐徐道來冬殃。

什么是一個OC的對象?

在OC2.0中囚痴,對象的定義是:


typedef struct objc_class *Class;
typedef struct objc_object *id;

@interface Object { 
    Class isa; 
}

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

struct objc_object {
private:
    isa_t isa;
}

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
}


可以看到我們所使用的OC對象其實是一個結構體,它里面的第一個變量就是一個指向類地址的isa指針审葬。而且我們的類也是一個對象深滚,他繼承自objc_object。同時它的isa指針指向meta class涣觉。下面看這張經(jīng)典的圖痴荐。

isa指針

圖中虛線表示isa指針,實線表示superclass指針官册。

1.Root class (class)其實就是NSObject生兆,NSObject是沒有超類的,所以Root class(class)的superclass指向nil膝宁。
2.每個Class都有一個isa指針指向自身的Meta class鸦难。
3.Root class(meta)的superclass指向Root class(class),也就是NSObject员淫,形成一個回路合蔽。
4.每個Meta class的isa指針都指向Root class (meta)。

這個時候再來回歸我們的問題介返,什么是一個OC的對象?

答:一個首地址指向類地址的結構體拴事。

注:如果還想繼續(xù)深入可以參考霜神這片文章

對象怎么去調(diào)用一個方法?

上面我們說到了圣蝎,每個實例對象都有一個isa指針指向類對象刃宵。
為什么要指向類對象呢?因為所有的實例方法列表都存儲在類對象中徘公,類方法存儲在元類中组去。
當我們?nèi)フ{(diào)用一個實例方法時,是通過實例對象的isa找到類步淹,然后再去尋找方法的實現(xiàn)从隆。
網(wǎng)上有很多的文章可供參考,在此不做細談缭裆。

對象怎么去獲取一個屬性的值键闺?

我們已經(jīng)知道了OC的對象其實是一個結構體,且實例對象的方法存儲在類對象中澈驼,類對象和元類對象在全局中只有一份辛燥,所以實例對象的屬性肯定是存儲在實例對象中。

下面我們來證明:
首先:屬性 = Ivar + get +set;

@interface IntClass : NSObject{
    
    @public
    int value1;
}
@end

@implementation IntClass

@end

@interface CharClass : NSObject {
    
    @public
    char value1;
    char value2;
}

@end

我們定義了兩個類IntClass和CharClass挎塌,他們分別有一個int型實例變量value1和兩個char型實例變量value1徘六,value2。
接下來看我的測試代碼:

    CharClass *charObject = [CharClass new];
    charObject->value1 = 1;
    charObject->value2 = 2;
    int value = ((IntClass *)charObject)->value1;
測試代碼1

在這里你可以看到榴都,我把一個CharClass的對象強轉為IntClass的對象并且強制獲取的他value1待锈,沒有奔潰,而且有值為513嘴高。
這個513是不是有點眼熟竿音,剛好等于256*2+1,也就是說他去取值的時候拴驮,剛好把char類型的value2和value1的值當做了一個int類型來讀取春瞬。

這個時候我們再把實例變量換成屬性。

    CharClass *charObject = [CharClass new];
    charObject.value1 = 1;
    charObject.value2 = 2;
    struct object *obj = (__bridge struct object *)charObject;
    obj->isa = [IntClass class];
    int value = ((__bridge IntClass *)obj).value1;

struct object的定義如下:

struct object {

    Class isa;
};
測試代碼2

你會發(fā)現(xiàn)結果和上面一直套啤,但是如果你注釋掉第52行代碼宽气,你會發(fā)現(xiàn)value的值為1。

由此我們可以得出以下結論:
1.實例變量存儲在實例對象(結構體)中潜沦。
2.實例變量的獲取方式為實例對象的地址+offset抹竹。
如圖所示:

示意圖1

因為char類型占一個字節(jié),而一個int占4個字節(jié)止潮。所以窃判,當我們把isa指向IntClass或者用實例變量來強制訪問IntClass的value1值時,其實是把value1和value2當做了一個整體來讀取喇闸,即*(int *)(charObject+8)袄琳。注:一個指針占8個字節(jié),Class其實是一個struct objc_class的指針燃乍。


示意圖2

這個時候再回到最開始的問題唆樊,也就是sunny的考試題。

@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Sark
- (void)speak {
   NSLog(@"my name's %@", self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
   [super viewDidLoad];
   id cls = [Sark class];
   void *obj = &cls;
   [(__bridge id)obj speak];
}
@end

很顯然obj滿足了一個OC的基本條件刻蟹,擁有指向類對象的指針逗旁。
但是此時這個類對象有點特殊,因為他沒有指向堆區(qū)舆瘪,而是指向了棧區(qū)片效。堆是從低地址向高地址生長,而棧是從高地址向低地址生長英古。因此obj+8等于向棧底偏移8個單位淀衣。那么這個時候他指向了哪里呢?我們用clang命令重寫看看召调。

示意圖3

我們可以看到viewDidLoad方法中有兩個隱藏參數(shù):self膨桥,_cmd在棧底蛮浑,然后是super標識符的構成,self只嚣,ViewController類沮稚。
所以此時的棧分布圖如下:


示意圖4

注:引用自霜神博客。

證明圖如下所示:


示意圖5

所以册舞,此時的obj對象的name屬性就是cls的地址再偏移8個字節(jié)蕴掏,也就是剛好是self的地址。所以此時輸出的是self的信息环础。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末囚似,一起剝皮案震驚了整個濱河市剩拢,隨后出現(xiàn)的幾起案子线得,更是在濱河造成了極大的恐慌,老刑警劉巖徐伐,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贯钩,死亡現(xiàn)場離奇詭異,居然都是意外死亡办素,警方通過查閱死者的電腦和手機角雷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來性穿,“玉大人勺三,你說我怎么就攤上這事⌒柙” “怎么了吗坚?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呆万。 經(jīng)常有香客問我商源,道長,這世上最難降的妖魔是什么谋减? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任牡彻,我火速辦了婚禮,結果婚禮上出爹,老公的妹妹穿的比我還像新娘庄吼。我一直安慰自己,他們只是感情好严就,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布霸褒。 她就那樣靜靜地躺著,像睡著了一般盈蛮。 火紅的嫁衣襯著肌膚如雪废菱。 梳的紋絲不亂的頭發(fā)上技矮,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音殊轴,去河邊找鬼衰倦。 笑死,一個胖子當著我的面吹牛旁理,可吹牛的內(nèi)容都是我干的樊零。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼孽文,長吁一口氣:“原來是場噩夢啊……” “哼驻襟!你這毒婦竟也來了?” 一聲冷哼從身側響起芋哭,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤沉衣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后减牺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豌习,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年拔疚,在試婚紗的時候發(fā)現(xiàn)自己被綠了肥隆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡稚失,死狀恐怖栋艳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情句各,我是刑警寧澤吸占,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站诫钓,受9級特大地震影響旬昭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜菌湃,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一问拘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惧所,春花似錦骤坐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至势似,卻和暖如春拌夏,著一層夾襖步出監(jiān)牢的瞬間僧著,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工障簿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盹愚,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓站故,卻偏偏與公主長得像皆怕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子西篓,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354