1.回顧
在之前的部落格中,對OC底層進行了一系列的源碼的探索分析,上一篇部落格也對一些面試題進行了回答和分析,本篇部落格繼續面試題分析!
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNCM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPRVGawd0YxB3MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL3EGO5ADOwUDMmR2M0IjY5MjZlRTYjFWZwEmYhlDM5IzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2. iOS面試題分析
2.1 ⽅法的本質?sel是什麼?IMP是什麼?兩者之間的關系⼜是什麼?
- 方法的本質:發送消息流程
- 快速消息查找 (
),objc_msgSend
緩存查找消息。cache_t
- 慢速消息查找(
)遞歸自己以及父類,自己找不到去父類緩存中找,依然找不到會進行父類慢速查找,直到找到nil。lookUpImpOrForward
- 查找不到消息進行動态方法解析(
/resolveInstanceMethod
)。resolveClassMethod
的過程中如果沒有找到方法,會調用resolveClassMethod
。resolveInstanceMethod
- 消息快速轉發(
),相當于找消息備用接收者。forwardingTargetForSelector
- 消息慢速轉發(
&methodSignatureForSelector
),在仍然沒有解決問題後在forwardInvocation
的時候會再進行一次慢速消息查找(這次不進行消息轉發)。methodSignatureForSelector
- 最後仍然沒有解決問題會進入
抛出異常。doesNotRecognizeSelector
-
是方法編号,在sel
期間就編譯進入了記憶體。read_images
-
就是函數實作指針 ,找imp
imp
就是找函數的過程。
可以将
了解為書本的目錄,sel-imp
書本目錄的名稱,sel
就是書本的⻚碼。查找具體的函數就是想看這本書裡面具體篇章的内容。imp
- 1:我們⾸先知道想看什麼 – >
tittle (sel)
- 2:根據⽬錄對應的⻚碼 – >
(imp)
- 3:翻到具體的内容
-
與imp
的關系SEL
: ⽅法編号SEL
: 函數指針位址IMP
相當于書本⽬錄的名稱SEL
: 相當于書本⽬錄的⻚碼IMP
- ⾸先明⽩我們要找到書本的什麼内容 (
⽬錄⾥⾯的名稱)sel
- 通過名稱找到對應的本⻚碼 (
)imp
- 通過⻚碼去定位具體的内容
2.2 能否向編譯後的得到的類中增加執行個體變量?能否向運⾏時建立的類中添加執行個體變量
不能向編譯後的得到的類中增加執行個體變量隻要内沒有注冊到記憶體還是可以添加:我們編譯好的執行個體變量存儲的位置在
原因
,⼀旦編譯完成,記憶體結構就完全确定了,是⽆法進行任何修改的。
ro
可以通過關聯對象的方式添加屬性,方法等
主要用到了
,
objc_setAssociatedObject
以及
objc_getAssociatedObject
方法
objc_removeAssociatedObjects
當我們的對象釋放的時候 -->
dealloc
1:
c++
函數釋放:
object_cxxDestruct
2: 移除關聯屬性 :
_object_remove_assocations
3: 将弱引⽤⾃動設定
nil
:
weak_clear_no_lock(&table.weak_table, (id)this)
4: 引⽤計數處理:
table.refcnts.erase(this)
4: 銷毀對象:
free(obj)
2.3 [self class]和[super class]的差別以及原理分析。
我先來看看如下,代碼
@implementation LGTeacher
- (instancetype)init{
self = [super init];
if (self) {
NSLog(@"%@ - %@",[self class],[super class]);
}
return self;
}
代碼運作結果如下:
2021-07-30 10:49:10.213169+0800 ObjcBuild[65615:2453340] LGTeacher--LGTeacher
列印結果可以了解,這
[self class]
列印就很懵了
[super class]
太意想不到了,那麼一下看看,是
clang
objc_msgSendSuper
之後 debug
一下,彙編跟蹤看看
不看不要緊,一看更懵逼!什麼鬼👻???不是發送消息嗎?怎麼又變成
objc_msgSendSuper
了啊!現在腦殼嗡嗡的!百思不得其解。
objc_msgSendSuper2
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
-
是class
的方法,NSObject
的隐藏參數是class
,id self
。是以SEL _cmd
就是發送消息[self class]
,消息接受者是objc_msgSend
和方法編号self
。是以傳回class
。LGTeacher
- 對于
來說它是沒有這個參數的。它不是參數名,是一個super
。在編譯器關鍵字
中編譯後調用的是clang
方法。objc_msgSendSuper
objc_msgSendSuper
有兩個參數
objc_super
和
SEL
-
objc_super
/// Specifies the superclass of an instance.
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
#if !defined(__cplusplus) && !__OBJC2__
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
};
#endif
其中的一個參數
receiver
是消息接收者,
super_class
為第一個被查找的類,但是實際它調用的是
objc_msgSendSuper2
,上面也驗證過了。
-
objc_msgSendSuper2
// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id _Nullable
objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
從源碼的注釋中也可以發現
super_class
應該就是目前類。
-
與objc_msgSendSuper
實作如下:objc_msgSendSuper2
ENTRY _objc_msgSendSuper
UNWIND _objc_msgSendSuper, NoFrame
//p0存儲receiver,p16存儲class
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
//跳轉到 L_objc_msgSendSuper2_body 的實作
b L_objc_msgSendSuper2_body
END_ENTRY _objc_msgSendSuper
// no _objc_msgLookupSuper
ENTRY _objc_msgSendSuper2
UNWIND _objc_msgSendSuper2, NoFrame
#if __has_feature(ptrauth_calls)
ldp x0, x17, [x0] // x0 = real receiver, x17 = class
//讀取
add x17, x17, #SUPERCLASS // x17 = &class->superclass
ldr x16, [x17] // x16 = class->superclass
AuthISASuper x16, x17, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS
LMsgSendSuperResume:
#else
//ldp讀取兩個寄存器,将objc_super解析成receiver和class
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
//通過class找到superclass
ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
#endif
L_objc_msgSendSuper2_body:
//查找緩存
CacheLookup NORMAL, _objc_msgSendSuper2, __objc_msgSend_uncached
END_ENTRY _objc_msgSendSuper2
從上面
arm64
的彙編源碼可以知道
_objc_msgSendSuper
跳轉到
_objc_msgSendSuper2
,差別是
_objc_msgSendSuper
直接調用,
objc_msgSendSuper2
通過
cls
擷取了
superClass
。也就是說
objc_msgSendSuper
傳遞的
objc_super
中
superClass
為父類,
objc_msgSendSuper2
傳遞的
objc_super
中
superClass
為自己,在彙編代碼中進行了父類的擷取。
那麼中
[super class]
決定了消息的接收者,從上面的解釋中也可以知道,這裡的接受者還是
receiver
也就是
self
LGTeacher
是以也列印的是
[super class]
。
LGTeacher
- 在
中實作源碼如下:llvm
更多内容持續更新
🌹 喜歡就點個贊吧👍🌹
🌹 覺得有收獲的,可以來一波,收藏+關注,評論 + 轉發,以免你下次找不到我😁🌹
🌹歡迎大家留言交流,批評指正,互相學習😁,提升自我🌹