1. 預備知識
在看原始碼之前我們先看一下objc中對於ivar,method,property的原始碼,由於網上現在很多關於runtime還在用objc2.0之前的原始碼,本來也打算這麼做,但是本著匠心精神,做事要嚴謹,還是把新的程式碼給翻出來了
1.1 ivar
1 2 3 4 5 6 7 8 |
struct ivar_t { int32_t *offset; const char *name; const char *type; uint32_t alignment_raw; uint32_t size; uint32_t alignment() { if (alignment_raw == ~(uint32_t)0) return 1U |
ivar關鍵的屬性有3個跟今天要講的有關係的屬性是name,type,offset,分別對應ivar的名字,型別和偏移量,偏移量這個說來話長,想仔細深入的推薦看《程式設計師的自我修養》,你會在裡面的到一個滿意的解答
1.2 property
1 2 3 4 |
struct property_t { const char *name; const char *attributes; }; |
每一個property中都有一個name代表著屬性名,attributes就是上一節我們說到的關於一個屬性的宣告描述,如屬性的原子性、記憶體管理規則等描述都存放在這裡
1.3 Method
關於method要說的東西比較多,我們先看一下method的原始碼
1 2 3 4 5 6 7 8 9 |
struct method_t { SEL name; const char *types; IMP imp; struct SortBySELAddress : public std::binary_function { bool operator() (const method_t |
在這裡我們需要了解SEL和IMP分別是什麼:
在執行的時候,runtime會為每一個class準備一張virtual table表,表格裡以字串當key,每個key會對應到C function的指標位置。在runtime裡,將C function 定義為IMP,將key的C字串定義為SEL,叫做selector type:
SEL: 方法ID
1 |
typedef struct objc_selector *SEL; |
1 2 3 4 |
struct objc_selector { char *name;// 名稱 char *types;// 型別 }; |
IMP:函式指標 C function Point
1 |
typedef id (*IMP)(id, SEL, ...); |
瞭解了SEL和IMP之後我們再來看method的定義,不難看出method中有方法名的字串和方法的訪問地址,順便儲存了方法的型別,當然還要有返回型別和引數型別,這樣就構成了一個method。
既然預備工作已經做好了,我們就來看YYModel的對這三項的封裝
2. YYClassIvarInfo
1 2 3 4 5 6 |
/** Instance variable information. 增加了對IVar的描述 */ @interface YYClassIvarInfo : NSObject @property (nonatomic, assign, readonly) Ivar ivar; /// |
這裡為什麼要對這個ivar的記憶體偏移地址和型別等這些,這裡這樣做是為了後面處理起來的時候,可以直接在外面暴露的地方獲取到這些屬性,而不需要再用runtime獲取對應的內容,也可以增加記憶體的命中率。(目前來說讀原始碼到這裡的理解是這樣,後面讀著如果有其他理解我會進行修改)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@implementation YYClassIvarInfo - (instancetype)initWithIvar:(Ivar)ivar { // 判斷是否為一個合法的ivar if (!ivar) return nil; self = [super init]; _ivar = ivar; // 獲得ivar的名稱 const char *name = ivar_getName(ivar); if (name) { // 轉化const char 屬性為NSString _name = [NSString stringWithUTF8String:name]; } // 取得相對偏移量,這樣做可以增加命中率 _offset = ivar_getOffset(ivar); const char *typeEncoding = ivar_getTypeEncoding(ivar); if (typeEncoding) { // 獲得編碼的同時 也轉為YYType _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; _type = YYEncodingGetType(typeEncoding); } return self; } @end |
3 YYClassMethodInfo
1 2 3 4 5 |
/** 增加對方法的描述資訊 */ @interface YYClassMethodInfo : NSObject @property (nonatomic, assign, readonly) Method method; /// *argumentTypeEncodings; /// |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
@implementation YYClassMethodInfo - (instancetype)initWithMethod:(Method)method { // 判斷method的合法性 if (!method) return nil; self = [super init]; _method = method; // 取得對應的sel _sel = method_getName(method); // 取得對應的IMP 即方法的記憶體地址 _imp = method_getImplementation(method); // 取得sel的名字 const char *name = sel_getName(_sel); if (name) { // 轉換成NSString _name = [NSString stringWithUTF8String:name]; } // 取得Method對應的型別 const char *typeEncoding = method_getTypeEncoding(method); if (typeEncoding) { _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; } // 取得返回內容的型別 char *returnType = method_copyReturnType(method); if (returnType) { _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; free(returnType); } // 迴圈取得一個方法的引數,並且取得每一個引數的型別,加入到陣列中 unsigned int argumentCount = method_getNumberOfArguments(method); if (argumentCount > 0) { NSMutableArray *argumentTypes = [NSMutableArray new]; for (unsigned int i = 0; i |
3. YYClassPropertyInfo
1 2 3 4 5 |
/** 屬性的描述資訊. */ @interface YYClassPropertyInfo : NSObject @property (nonatomic, assign, readonly) objc_property_t property; /// |
4. 最後
有人說每次篇幅比較少而且講的比較淺,那肯定是啊,都是從基礎的地方開始分析,才能往上擴充,本來這次的目的就是寫一篇小白讀原始碼的,但後面的內容也會多分析一下深層的,謝謝支援