Runtime.png
1、Runtime
1.1、Runtime :Objective-C運(yùn)行時(shí),指代碼跑起來(lái)了.被裝載到內(nèi)存中去的過(guò)程,具有動(dòng)態(tài)性,能夠進(jìn)行消息傳遞、動(dòng)態(tài)方法解析、消息轉(zhuǎn)發(fā)、類(lèi)型編碼、聲明屬性等一系類(lèi)操作。
1.2、Runtime有兩個(gè)版本:一個(gè)現(xiàn)在(modern)版本,一個(gè)傳統(tǒng)(legacy)版本
傳統(tǒng)版本對(duì)應(yīng)的編程接?:Objective-C 1.0。
現(xiàn)?版本對(duì)應(yīng)的編程接?:Objective-C 2.0 。
傳統(tǒng)版本?于Objective-C 1.0, 32位的Mac OS X的平臺(tái)上。
現(xiàn)?版本:iPhone程序和Mac OS X v10.5 及以后的系統(tǒng)中的 64 位程序 。
1.3、Runtime交互
Objective-C程序可在三個(gè)不同
的層次上與Runtime進(jìn)行交互:通過(guò)Objective-C源代碼、Foundation框架的NSObject類(lèi)中定義的方法、直接調(diào)用運(yùn)行時(shí)函數(shù)。
Runtime交互結(jié)構(gòu).png
Runtime交互三種路徑.png
2、objc_msgSend
解釋?zhuān)?strong>發(fā)送一個(gè)帶有簡(jiǎn)單返回值的消息到一個(gè)類(lèi)的實(shí)例
//發(fā)送一個(gè)帶有簡(jiǎn)單返回值的消息到一個(gè)類(lèi)的實(shí)例。
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
objc_msgSend:
self-->self指向接收消息的類(lèi)實(shí)例的指針
op-->處理消息的方法的選擇器
...-->方法參數(shù)的變量參數(shù)列表
//發(fā)送一個(gè)帶有簡(jiǎn)單返回值的消息到類(lèi)的實(shí)例的超類(lèi)。
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
objc_msgSendSuper:
super-->指向一個(gè)objc_super數(shù)據(jù)結(jié)構(gòu)的指針。傳遞標(biāo)識(shí) 消息被發(fā)送到的上下文,包括接收消息的類(lèi)的實(shí)例
消息和開(kāi)始搜索方法實(shí)現(xiàn)的超類(lèi)。
op-->SEL類(lèi)型的指針。傳遞將處理消息的方法的選擇器。
...-->方法參數(shù)的變量參數(shù)列表
接下來(lái)通過(guò)源碼來(lái)查看:
//main函數(shù)中方法的調(diào)用
#import <Foundation/Foundation.h>
#import <objc/message.h>
@interface LGTeacher : NSObject
- (void)sayHello;
@end
@implementation LGTeacher
- (void)sayHello{
NSLog(@"666 %s",__func__);
}
@end
@interface LGPerson : LGTeacher
- (void)sayHello;
- (void)sayNB;
@end
@implementation LGPerson
- (void)sayNB{
NSLog(@"666");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
LGTeacher *teacher = [LGTeacher alloc];
[person sayNB];
[person sayHello];
}
return 0;
}
通過(guò)Clang查看源碼:
__OBJC_RW_DLLIMPORT void objc_msgSend(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);
__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
LGTeacher *teacher = ((LGTeacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGTeacher"), 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"));
當(dāng)遇到方法調(diào)用時(shí),編譯器生成對(duì)對(duì)應(yīng)
objc_msgSend 、 objc_msgSend_stret, objc_msgSendSuper、 objc_msgSendSuper_stret。
其中
發(fā)送到超類(lèi)的消息使用objc_msgSendSuper
其他消息發(fā)送使用 objc_msgSend
將數(shù)據(jù)結(jié)構(gòu)作為返回值的方法
objc_msgSendSuper_stret
objc_msgSend_stret
在main方法中調(diào)用objc_msgSend ,objc_msgSendSuper,此時(shí)發(fā)現(xiàn)使用clang會(huì)報(bào)錯(cuò),無(wú)法生成main.cpp文件
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
LGTeacher *teacher = [LGTeacher alloc];
[person sayNB];
[person sayHello];
//2.消息發(fā)送
objc_msgSend(person, sel_registerName("sayNB"));
struct objc_super lgsuper;
lgsuper.receiver = person;//消息的接收者還是person
lgsuper.super_class = [LGTeacher class];//告訴父類(lèi)是誰(shuí)
objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));
}
return 0;
}
objc_super結(jié)構(gòu)
```#ifndef OBJC_SUPER
#define OBJC_SUPER
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
receiver:消息的接收者
super_class:指定消息的接收者超類(lèi)
輸出打?。?/p>
2021-06-27 16:36:12.851501+0800 001-運(yùn)行時(shí)感受[8467:207092] 666
2021-06-27 16:36:12.851894+0800 001-運(yùn)行時(shí)感受[8467:207092] 666 -[LGTeacher sayHello]
2021-06-27 16:36:12.851951+0800 001-運(yùn)行時(shí)感受[8467:207092] 666
2021-06-27 16:36:12.851992+0800 001-運(yùn)行時(shí)感受[8467:207092] 666 -[LGTeacher sayHello]
總結(jié):
[person sayNB]等價(jià)于objc_msgSend(person, sel_registerName("sayNB"))
無(wú)論是[person sayHello]還是objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"))執(zhí)行的都是父類(lèi)中的實(shí)現(xiàn)
3.objc_msgSend源碼查看
搜索objc_msgSend,打開(kāi)源碼文件objc-msg-arm64.s
_objc_msgSend
對(duì)應(yīng)流程圖.png
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
cmp p0, #0 // nil check and tagged pointer check 判斷接受者是否存在
#if SUPPORT_TAGGED_POINTERS //是否支持tagged_pointers對(duì)象
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero //直接返回空 LReturnZero
#endif
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13, 1, x0 // p16 = class
LGetIsaDone: //獲取isa完畢LGetIsaDone
// calls imp or objc_msgSend_uncached
//開(kāi)啟緩存查找流程
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
LNilOrTagged
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged: //小對(duì)象或者空判斷
b.eq LReturnZero // nil check 直接返回空了LReturnZero
GetTaggedClass
b LGetIsaDone //小對(duì)象ISA處理
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
CacheLookup
開(kāi)啟緩存查找流程.png
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
mov x15, x16 // stash the original isa
LLookupStart\Function:
// p1 = SEL, p16 = isa
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
ldr p10, [x16, #CACHE] // p10 = mask|buckets
lsr p11, p10, #48 // p11 = mask
and p10, p10, #0xffffffffffff // p10 = buckets
and w12, w1, w11 // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
ldr p11, [x16, #CACHE] // p11 = mask|buckets
#if CONFIG_USE_PREOPT_CACHES
#if __has_feature(ptrauth_calls)
tbnz p11, #0, LLookupPreopt\Function
and p10, p11, #0x0000ffffffffffff // p10 = buckets
#else
and p10, p11, #0x0000fffffffffffe // p10 = buckets
tbnz p11, #0, LLookupPreopt\Function
#endif
eor p12, p1, p1, LSR #7
and p12, p12, p11, LSR #48 // x12 = (_cmd ^ (_cmd >> 7)) & mask
#else
and p10, p11, #0x0000ffffffffffff // p10 = buckets
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
ldr p11, [x16, #CACHE] // p11 = mask|buckets
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 p13, p10, p12, LSL #(1+PTRSHIFT)
// p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// do {
1: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket--
cmp p9, p1 // if (sel != _cmd) {
b.ne 3f // scan more
// } else {
2: CacheHit \Mode // hit: call or return imp
// }
3: cbz p9, \MissLabelDynamic // if (sel == 0) goto Miss;
cmp p13, p10 // } while (bucket >= buckets)
b.hs 1b
// wrap-around:
// p10 = first bucket
// p11 = mask (and maybe other bits on LP64)
// p12 = _cmd & mask
//
// A full cache can happen with CACHE_ALLOW_FULL_UTILIZATION.
// So stop when we circle back to the first probed bucket
// rather than when hitting the first bucket again.
//
// Note that we might probe the initial bucket twice
// when the first probed slot is the last entry.
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
add p13, p10, w11, UXTW #(1+PTRSHIFT)
// p13 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))
// p13 = buckets + (mask << 1+PTRSHIFT)
// see comment about maskZeroBits
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p13, p10, p11, LSL #(1+PTRSHIFT)
// p13 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = first probed bucket
// do {
4: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket--
cmp p9, p1 // if (sel == _cmd)
b.eq 2b // goto hit
cmp p9, #0 // } while (sel != 0 &&
ccmp p13, p12, #0, ne // bucket > first_probed)
b.hi 4b
LLookupEnd\Function:
objc_msgSend總體流程圖:
objc_msgSend流程分析.png





