瞅一瞅、看一看、isa指針怎么辦?
關于這個問題,首先要了解異或、與、或運算。可以查看我往期的文章,有詳細介紹。
1:要搞清楚isa指針,必須要知道的幾個變量
// 簡化了代碼如下
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
// 簡化了代碼如下
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
};
也就是比較簡單的ISA_MASK
,isa
。這里可以暫且先可以這樣記住isa的指向問題。實例對象的isa指向類對象,類對象的isa指向元類,元類對象指向根元類,根元類指向自己。姑且先按下圖的方式記住。
2:用代碼驗證isa
指針問題
2.1 先添加兩個類,代碼實現如下
@interface Person : NSObject
@property (nonatomic, strong) NSString * personName;
@property (nonatomic, strong) NSString * personAges;
@property (nonatomic, strong) NSString * personAddrress;
-(void)personIstanceMethod1;
-(void)personlnstanceMethod2;
-(void)personlnstanceMethod3;
+(void)personClassMethod1;
+(void)personClassMethod2;
+(void)personClassMethod3;
@end
@interface Girl : Person
@property (nonatomic, strong) NSString * girlName;
@property (nonatomic, strong) NSString * girlAges;
@property (nonatomic, strong) NSString * girlAddrress;
-(void)girlnstanceMethod1;
-(void)girlnstanceMethod2;
-(void)girlnstanceMethod3;
+(void)girClassMethod1;
+(void)girClassMethod2;
+(void)girClassMethod3;
@end
2.2 驗證代碼如下
Girl * girls = [[Girl alloc] init];
Class instanceClass = [girls class];
Class clsClass = [Girl class];
Class metaClass = object_getClass(clsClass);
NSLog(@"--instanceClass:%p----clsClass:%p-----metaClass:%p",instanceClass,clsClass,metaClass);
2.3 斷點查看各對象的isa指針的值
(lldb) p/x girls->isa
(Class) $0 = 0x00000001067bd750 Girl
(lldb) p/x instanceClass
(Class) $1 = 0x00000001067bd750 Girl
(lldb) p/x clsClass
(Class) $2 = 0x00000001067bd750 Girl
(lldb) p/x metaClass
(Class) $3 = 0x00000001067bd778
(lldb) p/x object_getClass(girls)
(Class) $4 = 0x00000001067bd750 Girl
這里盡然可以發現girls->isa
指針地址和clsClass
對象的isa地址是一樣的。別著急,下面繼續驗證。
這里意外的發現instanceClass
和clsClass
對象地址是相同的。也就是說都是類對象。同等于[Girls class]
。
順便也可得出object_getClass
返回對象的結論:
如果傳入的是實例對象
返回的結果就類對象
如果傳入的是類對象
返回的結果就元類對象
2.4 言歸正傳話說ISA指針
實例對象的isa
指針地址是0x00000001067bd750
在第一部分中關于objc_object
源碼中有一個私有變量
private:
isa_t isa;
和他相關聯的isa_t
是一個聯合體如下
這里需要注意的是bits
屬性
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
};
其中objc_object公開的關于isa的方法
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
方法ISA()源碼如下
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
}
注意這里有個宏定義
SUPPORT_INDEXED_ISA
表示 isa_t 中存放的 Class 信息是 Class 的地址,還是一個索引(根據該索引可在類信息表中查找該類結構地址)。經測試,iOS 設備上 SUPPORT_INDEXED_ISA 是 0。
TaggedPointer此處不做介紹
最后這個ISA()
執行到
return (Class)(isa.bits & ISA_MASK);
可以看到&ISA_MASK。接著我們會到2.4 斷點查看各對象的isa指針的值
小節。用girl的實例對象isa & ISA_MASK。如果過你忘了ISA_MASK
是什么了,請翻到最上方可查看定義。
代碼如下
p/x 0x00000001067bd750 & 0x0000000ffffffff8
(long) $5 = 0x00000001067bd750
會發現&
之后沒變。是不是感覺& ISA_MASK 是不是沒用。
記住這個問題后,在繼續驗證類對象的isa和元類對象的isa關系
由于斷點沒法看Class
對象的isa
需要自定義一個結構體
struct custom_objc_class {
Class isa;
};
看看校驗代碼
Girl * girls = [[Girl alloc] init];
Class instanceClass = [girls class];
Class clsClass = [Girl class];
struct custom_objc_class * _objc_class = (__bridge custom_objc_class *)(clsClass);
Class metaClass = object_getClass(clsClass);
校驗代碼如下
(lldb) p/x _objc_class->isa
(Class) $0 = 0x000000010a2168c0
(lldb) p/x metaClass
(Class) $1 = 0x000000010a2168c0
(lldb) p/x 0x000000010a2168c0 & 0x0000000ffffffff8
(long) $2 = 0x000000010a2168c0
第一眼沒變化,是不是,類對象的isa和原來的地址是一樣的。即使是&ISA_MASK了之后也沒感覺。。。。。。
各位看官請繼續向下看!!!
以上是我創建的iOS項目,為了驗證問題,繼續創建一個命令行項目。
3:繼續用命令行項目的代碼驗證isa
指針問題
3.1命令行項目驗證實例對象的isa
所有的步驟和創建的兩個類都一樣。就省略一下
直接上運行的代碼
#import "Girl.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Girl * girls = [[Girl alloc] init];
Class instanceClass = [girls class];
Class clsClass = [Girl class];
Class metaClass = object_getClass(clsClass);
NSLog(@"--instanceClass:%p----clsClass:%p-----metaClass:%p",instanceClass,clsClass,metaClass);
}
return 0;
}
直接打印對象的地址和&ISA_MASK的操作
(lldb) p/x girls->isa
(Class) $0 = 0x001d8001000021d9 Girl
(lldb) p/x clsClass
(Class) $1 = 0x00000001000021d8 Girl
(lldb) p/x 0x001d8001000021d9 & 0x00007ffffffffff8
(long) $2 = 0x00000001000021d8
注意這里跑的命令行項目。所有要用0x00007ffffffffff8
值來做為ISA_MASK。
3.2命令行項目驗證類對象的isa
和在iOS項目下一樣的創建一個結構體
struct custom_objc_class {
Class isa;
};
Girl * girls = [[Girl alloc] init];
Class instanceClass = [girls class];
Class clsClass = [Girl class];
struct custom_objc_class * _objc_class = (__bridge custom_objc_class *)(clsClass);
Class metaClass = object_getClass(clsClass);
直接打印輸出如下
(lldb) p/x cs_objc_class->isa
(Class) $0 = 0x00000001000021b8
(lldb) p/x metaClass
(Class) $1 = 0x00000001000021b8
(lldb) p/x 0x00007ffffffffff8 & 0x00000001000021b8
(long) $2 = 0x00000001000021b8
這個很奇怪哦,類對相關isa竟然和元類對象的地址是一樣的。。。。是不是偶然的
小結:
所以證明了實例對象的isa&ISA_MASK 可以得到類對象的地址,
當然也可以同樣的驗證類的isa&ISA_MASK可以得到元類對象的地址。
至于為什么能&ISA_MASK能得到對象的地址。本文暫不錯解釋,后面會有詳細說明,可以先說一點點,這里iOS對arm64的isa指針做了優化。