因爲篇幅問題,會分三篇描述對應不一樣內容。輔助工具,底層原理,開發中要注意問題三個方面,談談對應的總結。本人以爲書只是一個索引,特別對於技術類書籍,基本都是經過書籍引入一些觀點,而後在經過其它第三方途徑進行擴展。因此本文描述內容不必定就是書本內容,會與自身實踐經驗,還有部份內容精簡和拓展。有些基礎概念第三方描述已足夠詳細,實例也足夠詳細,本文僅僅提出一些我的總結理解,不在重複描述具體功能原理。html
文字不如圖片直觀,因此先上一張本系列描述的觀點的思惟導圖,梳理脈絡。紅色部分爲本文內容梳理。ios
《思惟導圖連接(點我打開)最新版本》objective-c
在Objective-C中,任何類的定義都是對象。類和類的實例(對象)沒有任何本質上的區別。編程
isa:是一個Class 類型的指針. 每一個實例對象有個isa的指針,他指向對象的類,而Class裏也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表。當類方法被調用時,先會從自己查找類方法的實現,若是沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass).根元類的isa指針指向自己,這樣造成了一個封閉的內循環。異步
類比8位MCU的pc指針,我我的理解isa指針,在硬件層面上就是指向代碼的運行地址能夠試RAM地址或ROM地址,等同MCU彙編的PC指針,調用函數時,須要先把當前函數地址壓入堆棧,燃後放置新地址到PC指針,而後退去取回原堆棧內的地址。可是OC isa 在這個基礎上增長了繼承的概念。async
如下是實例引用流程:編程語言
每個對象本質上都是一個類的實例。其中類定義了成員變量和成員方法的列表。對象經過對象的isa指針指向類。函數
每個類本質上都是一個對象,類實際上是元類(meteClass)的實例。元類定義了類方法的列表。類經過類的isa指針指向元類。工具
全部的元類最終繼承一個根元類,根元類isa指針指向自己,造成一個封閉的內循環。atom
指一個程序在運行(或者在被執行)的狀態。也就是說,當你打開一個程序使它在電腦上運行的時候,那個程序就是處於運行時刻。在一些編程語言中,把某些能夠重用的程序或者實例打包或者重建成爲「運行庫"。這些實例能夠在它們運行的時候被鏈接或者被任何程序調用。
objective-c中runtime:是一套比較底層的純C語言API, 屬於1個C語言庫, 包含了不少底層的C語言API。 在咱們平時編寫的OC代碼中, 程序運行過程時, 其實最終都是轉成了runtime的C語言代碼。
RunTime基本經常使用法 (《iOS開發進階》頁碼:220 - 225 ps:這裏說得不夠細)
//1 動態增長變量 @property (nonatomic, assign) BOOL isNotIgnore; //runtime 動態綁定 屬性 - (BOOL)isNotIgnore{ //_cmd == @select(isIgnore); 和set方法裏一致 return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)setIsNotIgnore:(BOOL)isNotIgnore{ // 注意BOOL類型 須要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用錯,不然set方法會賦值出錯 objc_setAssociatedObject(self, @selector(isNotIgnore), @(isNotIgnore), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } //2 對象方法的交換 /** * 對象方法的交換 * * @param anClass 哪一個類 * @param method1Sel 方法1 * @param method2Sel 方法2 */ + (void)exchangeInstanceMethod:(Class)anClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel { Method method1 = class_getInstanceMethod(anClass, method1Sel); Method method2 = class_getInstanceMethod(anClass, method2Sel); method_exchangeImplementations(method1, method2); } //3 動態類方法的交換 /** * 類方法的交換 * * @param anClass 哪一個類 * @param method1Sel 方法1 * @param method2Sel 方法2 */ + (void)exchangeClassMethod:(Class)anClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel { Method method1 = class_getClassMethod(anClass, method1Sel); Method method2 = class_getClassMethod(anClass, method2Sel); method_exchangeImplementations(method1, method2); } //4 動態類方法的交換 /** * 對象方法重置 * * @param anClass 哪一個類 * @param method1Sel 方法1 */ + (void)setClassMethod:(Class)anClass oldMethodSel:(SEL) oldMethodSel newMethodSel:(SEL)newMethodSel { Method oldMethod = class_getInstanceMethod(anClass, oldMethodSel); Method newMethod = class_getInstanceMethod(anClass, newMethodSel); method_setImplementation(oldMethod, method_getImplementation(newMethod)); }
這個概念其實對應如今已經有點老了,32爲設備已在蘋果放棄支持的日程表上了。可是對於一些基礎仍是有必要了解一下。簡單就是在64位設備上,同一地址裂開2部分使用,那其中32位最基礎值類保存,另外位數存指針,索引表之類的數據,複用一個地址上的存儲空間。這點和mcu內存一個byte,低4位和高4分開存在2個0-16的數值,是相似的。
假設咱們要存儲一個NSNumber對象,其值是一個整數。正常狀況下,若是這個整數只是一個NSInteger的普通變量,那麼它所佔用的內存是與CPU的位數有關,在32位CPU下佔4個字節,在64位CPU下是佔8個字節的。而指針類型的大小一般也是與CPU位數相關,一個指針所佔用的內存在32位CPU下爲4個字節,在64位CPU下也是8個字節。
普通的iOS程序,若是沒有Tagged Pointer對象,從32位機器遷移到64位機器中後,雖然邏輯沒有任何變化,但這種NSNumber、NSDate一類的對象所佔用的內存會翻倍
爲了改進上面提到的內存佔用和效率問題,蘋果提出了Tagged Pointer對象。因爲NSNumber、NSDate一類的變量自己的值須要佔用的內存大小經常不須要8個字節,拿整數來講,4個字節所能表示的有符號整數就能夠達到20多億(注:2^31=2147483648,另外1位做爲符號位),對於絕大多數狀況都是能夠處理的。
__block修飾變量,在block中,使用能直接改變變量的值。
沒有__block修飾變量,在block使用,等於原變量copy了一個新變量,改變其值不影響原值。
這個問題出如今block替換代理時,引用變量致使控制器不釋放比較常見。由於copy致使原變量引用加一。
因此block引用self,必須加上弱引聲明。
__weak typeof(BaseViewController) *weakSelf = self;
本質緣由是由於對象和其餘數據類型在系統中的存儲空間不同,其它局部變量主要存放於棧中,而對象存儲於堆中,當代碼塊結束時這個代碼塊中涉及的全部局部變量會被回收,指向對象的指針也被回收,此時對象已經沒有指針指向,但依然存在於內存中,形成內存泄露。
每一個OC對象都有本身的引用計數器,是一個整數表示對象被引用的次數,即如今有多少東西在使用這個對象。對象剛被建立時,默認計數器值爲1,當計數器的值變爲0時,則對象銷燬。 在每一個OC對象內部,都專門有4個字節的存儲空間來存儲引用計數器。判斷對象要不要回收的惟一依據就是計數器是否爲0,若不爲0則存在。
block中,因爲對象會copy,全部會作成原變量引用加入,全部會致使不釋放。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(globalQueue, ^{ //子線程異步執行下載任務,防止主線程卡頓 NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"]; NSError *error; NSString *htmlData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; if (htmlData != nil) { dispatch_queue_t mainQueue = dispatch_get_main_queue(); //異步返回主線程,根據獲取的數據,更新UI dispatch_async(mainQueue, ^{ NSLog(@"根據更新UI界面"); }); } else { NSLog(@"error when download:%@",error); } });
- (void)groupSync { dispatch_queue_t disqueue = dispatch_queue_create("com.shidaiyinuo.NetWorkStudy", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t disgroup = dispatch_group_create(); dispatch_group_async(disgroup, disqueue, ^{ NSLog(@"任務一完成"); }); dispatch_group_async(disgroup, disqueue, ^{ sleep(8); NSLog(@"任務二完成"); }); dispatch_group_notify(disgroup, disqueue, ^{ NSLog(@"dispatch_group_notify 執行"); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ dispatch_group_wait(disgroup, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)); NSLog(@"dispatch_group_wait 結束"); }); }
@property (nonatomic, strong) dispatch_block_t dispatch_stopLoading_block; @weakify(self) if (self.dispatch_stopLoading_block) { dispatch_block_cancel(self.dispatch_stopLoading_block); self.dispatch_stopLoading_block = NULL; } self.dispatch_stopLoading_block = dispatch_block_create(0, ^{ @strongify(self) NSLog(@"任務一完成"); }); //任務延遲啓動 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), self.dispatch_stopLoading_block);
//因爲設定的信號值爲2,先執行兩個線程,等執行完一個,纔會繼續執行下一個,保證同一時間執行的線程數不超過2。 -(void)dispatchSignal{ //crate的value表示,最多幾個資源可訪問 dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //任務1 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 1"); sleep(1); NSLog(@"complete task 1"); dispatch_semaphore_signal(semaphore); });<br> //任務2 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 2"); sleep(1); NSLog(@"complete task 2"); dispatch_semaphore_signal(semaphore); });<br> //任務3 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 3"); sleep(1); NSLog(@"complete task 3"); dispatch_semaphore_signal(semaphore); }); }
原文:http://raychow.linkfun.top/2018/01/07/archives/1_ios/2017-section-2/index/