今天看了一些關於 Objc 相關的知識,也是一些基礎部分的知識。使用匯編來查看 alloc
的實現過程。bash
而後分享一下 init 到底幹了啥。架構
下面就一塊兒看看具體流程。函數
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "CTStudent.h"
#import <objc/runtime.h>
int main(int argc, char * argv[]) {
CTStudent *st = [CTStudent alloc];
NSLog(@"size %lu", class_getInstanceSize([st class]));
return 0;
}
複製代碼
爲了避免加載過多的變量及方法,我直接在Main
文件裏開始了。測試
說明:ui
st
的大小。那麼接下來就具體看看使用匯編來看看具體流程。atom
Notespa
注意請連接真機就行調試,不要用電腦,由於電腦和收回系統架構不一樣,彙編代碼也是不一樣的。3d
在此以前介紹幾個接下來要用的彙編命令調試
跳轉到指定的函數
中去方法走到這裏就返回
了,不在往下面走了 具體的能夠參考網上盡詳的資料。alloc
這個方法的實現。因此我使用符號斷點標記 alloc
, 只要通過這個方法的都會停留下來。運行,他會停留在 _objc_rootAlloc
這個函數這裏. code
那麼我繼續使用斷點符號把 _objc_rootAlloc
也就好了標記.
class_createInstance
的函數調用中去。Note:若是彙編沒有走到 class_createInstance
這一步以前,就不用啓用 class_createInstance
符號斷點,由於系統中的不少函數初始化也會走這個方法,因此我就在這個函數以前打了一個斷點,若是真的走到斷點符號前面的這個斷點,我就會使用 register read
來讀取當前內存中的值。那麼我爲何會在ret
設置斷點了,是由於alloc
調用完了會有返回值。
Objc_mseSend
函數。
Objc_mseSend
默認會有兩個值。
Objc_mseSend(id self, SEL _cmd)
* 第一個方法的調用者
* 第二個參數是方法編號
複製代碼
在內存寄存器中,x0
默認是方法調用者
, x1
是方法實現編號
. 那麼結合上圖能夠看到上面的 x0 地址,使用 po
命令便可查看對應的值。 咱們能夠看到是CTStudent
,正是咱們 alloc
的調用者. 若是肯定是咱們關注的對象,那麼咱們就須要ge 5. 我把 class_createInstance
加入斷點中。
class_createInstance
的調用流程中。
通過一系列的走位,最終被 return 出去了。
而後繼續,被 return 到了上一次。此時控制到輸出了
那麼,說明此時 上的 st
已經有大小了,大小位:8 字節。那麼爲啥我什麼都沒寫倒是 8 了,由於每一個類中都有一個 isa
, 是 8 字節大小。
摘自 Objc4-750 源碼
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
複製代碼
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
複製代碼
能夠看出 isa
是一個 uintptr_t
類型。
那麼根據彙編來看 alloc
的流程爲:
alloc -> _objc_rootAlloc -> class_createInstance.
固然這裏面確定省略了不少步驟,可是使用匯編來查看代碼的運行步驟是頗有趣的。不妨動手試試看。固然詳細的過程,我會以解析源碼的過程來闡述明白。
附帶今天跟蹤源碼畫的 alloc 走位圖
- (id)init {
return _objc_rootInit(self);
}
id _objc_rootInit(id obj)
{
return obj;
}
複製代碼
其實就是返回了本身。
那麼爲啥要這麼作了? 其實就是讓你在 init 中作一些初始化配置等操做。
想一想,咱們平時開發都在 init 中幹了啥了。
@interface Student : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSMapTable *mapTable;
- (void)save;
- (void)log;
@end
@implementation Student
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"CT_Kare";
self.mapTable = [[NSMapTable alloc] init];
}
return self;
}
- (void)save {
[self.mapTable setObject:self.name forKey:@"name"];
}
- (void)log {
NSLog(@"self.mapTable %@", self.mapTable);
}
@end
複製代碼
那麼,其實也是作了一些初始化操做。
總結:因此在初始化一個對象時,alloc
作了初始化準備,好比開闢對象空間,init Isa
,等操做。init 其實就是返回了初始化對象自己
,而後在 init 中作了一些初始化操做配置
而已。