運行時感受歌逢,通過clang底層編譯
編譯前的代碼
#import <Foundation/Foundation.h>
@interface DCPerson :NSObject
-(void)sayNB;
-(void)sayHello;
@end
@implementation DCPerson
-(void)sayNB{
NSLog(@"%s",__func__);
}
-(void)sayHello{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
DCPerson *person = [DCPerson alloc];
[person sayNB];
[person sayHello];
NSLog(@"Hello, World!");
}
return 0;
}
編譯后的代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
DCPerson *person = ((DCPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("DCPerson"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_yy9dm1g92sjdnrzk1pn6cgf80000gn_T_main_9aaf89_mi_2);
}
return 0;
}
- 1.調(diào)用sayNB方法進行等價互換objc_msgSend(person,sel_registerName("sayNB"));
objc_msgSend(person,sel_registerName("sayNB"));
- 2.使用objc_msgSendSuper
@interface Anthropoid : NSObject
-(void)sayHello;
@end
@implementation Anthropoid
-(void)sayHello{
NSLog(@"%s",__func__);
}
@end
@interface DCPerson :Anthropoid
-(void)sayNB;
@end
@implementation DCPerson
-(void)sayNB{
NSLog(@"%s",__func__);
}
@end
//消息的接受者還是自己 - 父類 - 直接找父類
struct objc_super lgsuper;
lgsuper.receiver = person;
lgsuper.super_class = [Anthropoid class];
objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));
//輸出
-[Anthropoid sayHello]
OC 方法 - 消息 (sel imp) sel -> imp -> 內(nèi)容
對象 - ISA - 方法(類) - cache_t - method_list(bits)
偽代碼實現(xiàn)
[person sayHello] -> imp (cache -> bucket (sel imp))
//獲取當前的對象
id person = 0x100000
//獲取isa
isa_t isa = 0x000000
//isa -> class -> cache
cache_t cache = isa + 16字節(jié)
//arm64
//mask|buckets 在一起的
buckets = cache & 0x0000ffffffffffff
//獲取mask
mask = cache LSR #48
//下標 = mask * & sel
index = mask & p1
//bucket 從buckets遍歷的開始(起始查詢的bucket)
bucket = buckets + index * 16 (sel imp = 16)
int count = 0;
//CheckMiss $0
do{
if(count == 2) goto CheckMiss
if(bucket == buckets){//進入第二層判斷
//bucket == 第一個元素
//bucket人為設置到最后一個元素
bucket = buckets + mask * 16;
count++;
}
// {imp,sel} = * --bucket
//緩存的查找的順序是:向前查找
bucket --;
imp = bucket.imp;
sel = bucket.sel;
}while(bucket.sel != _cmd)//bucket里面的sel是否匹配_cmd
//CacheHit $0
return imp;
CheckMiss:
CheckMiss(normal)
偽代碼自己完善下?妖爷??
一個bucket中存儲了16個字節(jié)的數(shù)據(jù)晃虫,因為sel和imp分別占用8字節(jié)
p12 = buckets + (mask << 1 + PTRSHIFT)
表示直接索引到最后一個元素
/*
//偽代碼實現(xiàn)
LLookupStart$1:
// p1 = SEL, p16 = isa
ldr p11, [x16, #CACHE] // p11 = mask|buckets
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
and p10, p11, #0x0000ffffffffffff // p10 = buckets
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
and p10, p11, #~0xf // p10 = buckets
and p11, p11, #0xf // p11 = maskShift
mov p12, #0xffff
lsr p11, p12, p11 // p11 = mask = 0xffff >> p11
and p12, p1, p11 // x12 = _cmd & mask
#else
#error Unsupported cache mask storage for ARM64.
#endif
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
3: // wrap: p12 = first bucket, w11 = mask
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
// p12 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p12, p12, p11, LSL #(1+PTRSHIFT)
// p12 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
LLookupEnd$1:
LLookupRecover$1:
3: // double wrap
JumpMiss $0
*/
[person sayHello] -> imp(cache -> bucket(sel,imp))
//獲取當前對象
id person = 0x1000000
//獲取isa
isa = 0x00000001
//isa -> class -> cache
cache_t cache = isa + 16字節(jié)
//arm64
//mask|buckets在一起的
buckets = cache & 0x0000ffffffffffff
//獲取mask
mask = cache LSR(邏輯右移) #48位
//下標 = mask & sel
index = mask & p1
//獲取單個的bucket迄汛,從buckets,遍歷的開始(起始查詢的bucket)
bucket = buckets + ((_cmd & mask) << (1+PTRSHIFT))
bucket = buckets + index * 16(sel,imp分別8字節(jié))
while(bucket.sel != _cmd){//bucket里面的sel是否匹配_cmd
//不滿足,沒有找到_cmd進入第二層判斷
if(bucket == buckets){
//bucket == 第一個元素
//bucket 人為設置到最后一個元素
p12 = buckets + (mask << 1+PTRSHIFT)
bucket = buckets + mask * 16(從后往前查找槽畔,減減(--操作))
}//{imp,sel} = *--bucket,緩存查找的順序:向前查找
bucket--;
imp = bucket.imp;
sel = bucket.sel;
}
return bucket.imp;//跳出循環(huán)即找到緩存中的方法栈妆,返回bucket.imp
goto CheckMiss:CheckMiss(normal)
消息發(fā)送到匯編之后,進入入口函數(shù)ENTRY,之后如何找到當前的類厢钧?
ENTRY _objc_msgSend
,之后通過ISA找到類
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13 // p16 = class
查看GetClassFromIsa_p16,$0表示當前對象傳過來的第一個參數(shù)為isa,和ISA_MASK(掩碼)進行與運算鳞尔,放到P16的位置
.macro GetClassFromIsa_p16 /* src */
#if SUPPORT_INDEXED_ISA
// Indexed isa
mov p16, $0 // optimistically set dst = src
tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // done if not non-pointer isa
// isa in p16 is indexed
adrp x10, _objc_indexed_classes@PAGE
add x10, x10, _objc_indexed_classes@PAGEOFF
ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS // extract index
ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:
#elif __LP64__
// 64-bit packed isa
and p16, $0, #ISA_MASK
#else
// 32-bit raw isa
mov p16, $0
#endif
CacheLookup:若bucket==buckets,表示是第一個元素早直,則到3f,將bucket移到最后一個元素寥假,比較當前是否是要查找的_cmd == bucket.imp,若是霞扬,緩存命中直接返回糕韧,若不是,則跳2f,先判斷是否遺失(是否為空喻圃,sel是否存在(bucket.sel == 0))萤彩,若不存在且bucket == buckets,即當前bucket為第一個元素(頂層),則跳JumpMiss $0斧拍,表示遺失了對象的方法即沒找到雀扶,若bucket != buckets,則向前繼續(xù)查找肆汹,進行bucket--愚墓,往前查找予权。
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
3: // wrap: p12 = first bucket, w11 = mask
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
// p12 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p12, p12, p11, LSL #(1+PTRSHIFT)
// p12 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
ldp p17, p9, [x12] // {imp, sel} = *bucket
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
LLookupEnd$1:
LLookupRecover$1:
3: // double wrap
JumpMiss $0
.endmacro