前言:這篇文章是我看李明傑老師的iOS底層原理班(下)/OC對象/關聯對象/多線程/內存管理/性能優化總結所得,斷斷續續歷時3個月左右,把課堂聽的東西給作了一下筆記。git
總結不易,耗時耗力,您的一顆小星星✨是我無限的動力。原文地址github
咱們常常會看一些面試題,可是好多面試題咱們都是知其然不知其因此然,你若是認真的看了我上面總結的幾十篇文章,那麼你也會知其因此然。面試
一、一個NSObject對象佔用多少內存?數據庫
系統分配了16個字節給NSObject對象(經過malloc_size函數得到),但NSObject對象內部只使用了8個字節的空間(64bit環境下,能夠經過class_getInstanceSize函數得到)編程
二、對象的isa指針指向哪裏?數組
三、OC的類信息存放在哪裏?緩存
具體實現請參考: 一、一個NSObject對象佔用多少內存 二、OC對象的分類安全
一、iOS用什麼方式實現對一個對象的KVO?(KVO的本質是什麼?)性能優化
_NSSetXXXValueAndNotify
函數
willChangeValueForKey
方法setAge
方法didChangeValueForKey
方法didChangeValueForKey
方法內部調用oberser的observeValueForKeyPath:ofObject:change:context:
方法二、如何手動觸發KVO?bash
手動調用willChangeValueForKey:和didChangeValueForKey:
三、直接修改爲員變量會觸發KVO麼?
不會觸發KVO
具體實現請參考:三、KVO實現原理
一、經過KVC修改屬性會觸發KVO麼?
會觸發KVO,由於KVC是調用set
方法,KVO就是監聽set
方法
二、KVC的賦值和取值過程是怎樣的?原理是什麼?
KVO的setValue:forKey原理
KVO的ValueforKey原理
具體實現請參考:四、KVC實現原理
一、Category的實現原理
二、Category和Class Extension的區別是什麼?
三、load、initialize方法的區別什麼?
1.調用方式
2.調用時刻
四、load、initialize的調用順序
1.load
2.initialize
五、如何實現給分類「添加成員變量」?
默認狀況下,由於分類底層結構的限制,不能添加成員變量到分類中。但能夠經過關聯對象來間接實現
關聯對象提供瞭如下API
添加關聯對象
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
得到關聯對象
id objc_getAssociatedObject(id object, const void * key)
移除全部的關聯對象
void objc_removeAssociatedObjects(id object)
複製代碼
具體實現請參考: 5.一、分類的實現原理 5.二、Load和Initialize實現原理
一、block的原理是怎樣的?本質是什麼?
二、block的(capture)
爲了保證block內部可以正常訪問外部的變量,block有個變量捕獲機制
三、Block類型有哪幾種 block有3種類型,能夠經過調用class方法或者isa指針查看具體類型,最終都是繼承自NSBlock類型
四、block的copy
在ARC環境下,編譯器會根據狀況自動將棧上的block複製到堆上,好比如下狀況
MRC下block屬性的建議寫法
@property (copy, nonatomic) void (^block)(void);
ARC下block屬性的建議寫法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
複製代碼
五、__block修飾符
__block
能夠用於解決block內部沒法修改auto變量值的問題
__block
不能修飾全局變量、靜態變量(static)
編譯器會將__block
變量包裝成一個對象
當__block變量在棧上時,不會對指向的對象產生強引用
當__block變量被copy到堆時
若是__block變量從堆上移除
六、循環引用
__unsafe_unretained typeof(self) weakSelf = self;
self.block = ^{
print(@"%p", weakSelf);
}
複製代碼
__weak typeof(self) weakSelf = self;
self.block = ^{
print(@"%p", weakSelf);
}
複製代碼
__block id weakSelf = self;
self.block = ^{
weakSelf = nil;
}
self.block();
複製代碼
具體實現請參考:六、Block底層解密
一、講一下 OC 的消息機制
二、消息轉發機制流程
消息發送階段
消息發送流程是咱們平時最常用的流程,其餘的像動態方法解析和消息轉發實際上是補救措施。具體流程以下
動態方法解析
開發者能夠實現如下方法,來動態添加方法實現
+resolveInstanceMethod:
+resolveClassMethod:
動態解析事後,會從新走「消息發送」的流程,從receiverClass的cache中查找方法這一步開始執行消息轉發
若是方法一個方法在消息發送階段沒有找到相關方法,也沒有進行動態方法解析,這個時候就會走到消息轉發階段了。
forwardingTargetForSelector
,返回值不爲nil時,會調用objc_msgSend
(返回值, SEL)methodSignatureForSelector
,返回值不爲nil,調用forwardInvocation:方法;返回值爲nil時,調用doesNotRecognizeSelector:
方法forwardInvocation:
方法中自定義任何邏輯三、什麼是Runtime?平時項目中有用過麼?
具體應用
四、super的本質
struct objc_super2
SEL
具體實現請參考:
一、講講 RunLoop,項目中有用到嗎? 一、定時器切換的時候,爲了保證定時器的準確性,須要添加runLoop 二、在聊天界面,咱們須要持續的把聊天信息存到數據庫中,這個時候須要開啓一個保活線程,在這個線程中處理
二、runloop內部實現邏輯
每次運行RunLoop,線程的RunLoop會自動處理以前未處理的消息,並通知相關的觀察者。具體順序
三、RunLoop與線程
四、timer 與 runloop 的關係?
解決定時器在滾動視圖上面失效問題NSTimer添加到兩種RunLoop中
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
複製代碼
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
複製代碼
五、RunLoop有幾種狀態
kCFRunLoopEntry = (1UL << 0), // 即將進入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即將處理Source
kCFRunLoopBeforeWaiting = (1UL << 5), //即將進入休眠
kCFRunLoopAfterWaiting = (1UL << 6),// 剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7),// 即將退出RunLoop
複製代碼
**六、RunLoop的mode的做用 **
RunLoop的mode的做用 系統註冊了5中mode
kCFRunLoopDefaultMode //App的默認Mode,一般主線程是在這個Mode下運行
UITrackingRunLoopMode //界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其餘 Mode 影響
UIInitializationRunLoopMode // 在剛啓動 App 時第進入的第一個 Mode,啓動完成後就再也不使用
GSEventReceiveRunLoopMode // 接受系統事件的內部 Mode,一般用不到
kCFRunLoopCommonModes //這是一個佔位用的Mode,不是一種真正的Mode
複製代碼
可是咱們只能使用兩種mode
kCFRunLoopDefaultMode //App的默認Mode,一般主線程是在這個Mode下運行
UITrackingRunLoopMode //界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其餘 Mode 影響
複製代碼
具體實現請參考:七、RunLoop實現原理
一、你理解的多線程? 二、iOS的多線程方案有哪幾種?你更傾向於哪種? 三、你在項目中用過 GCD 嗎? 四、GCD 的隊列類型 五、說一下 OperationQueue 和 GCD 的區別,以及各自的優點 六、線程安全的處理手段有哪些? 使用線程鎖
七、線程通信 線程間通訊的體現
一、NSThread 能夠先將本身的當前線程對象註冊到某個全局的對象中去,這樣相互之間就能夠獲取對方的線程對象,而後就可使用下面的方法進行線程間的通訊了,因爲主線程比較特殊,因此框架直接提供了在主線程執行的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
複製代碼
二、GCD
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});
複製代碼
一、使用CADisplayLink、NSTimer有什麼注意點? CADisplayLink、NSTimer會對target產生強引用,若是target又對它們產生強引用,那麼就會引起循環引用
二、介紹下內存的幾大區域
三、講一下你對 iOS 內存管理的理解 在iOS中,使用引用計數來管理OC對象的內存
內存管理的經驗總結
能夠經過如下私有函數來查看自動釋放池的狀況 extern void _objc_autoreleasePoolPrint(void);
四、ARC 都幫咱們作了什麼 LLVM + Runtime
五、weak指針的實現原理 runtime維護了一個weak表,用於存儲指向某個對象的全部weak指針。weak表實際上是一個hash(哈希)表,key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象指針的地址)數組
六、autorelease對象在什麼時機會被調用release
autoreleased 對象是在 runloop 的即將進入休眠時進行釋放的
七、方法裏有局部對象, 出了方法後會當即釋放嗎 在ARC狀況下會當即釋放 在MRC狀況下,對象是在 runloop 的即將進入休眠時進行釋放的
文章中能夠提煉出來的題目太多了,我這裏也就簡單的總結幾道題,想要了解具體實現請到個人github中找到相關文章進行閱讀。歡迎點贊哦,若是裏面有什麼我理解的不太正確,歡迎提出,咱們相互印證