review in 2018

設計模式

  • 設計模式

設計模式是一種編碼經(jīng)驗,就是用比較成熟的邏輯去處理某一種類型的事情。
1). MVC模式:Model View Control,把模型 視圖 控制器 層進行解耦合編寫。
2). MVVM模式:Model View ViewModel 把模型 視圖 業(yè)務邏輯 層進行解耦和編寫。
3). 單例模式:通過static關(guān)鍵詞,聲明全局變量。在整個進程運行期間只會被賦值一次。
4). 觀察者模式:KVO是典型的通知模式,觀察某個屬性的狀態(tài),狀態(tài)發(fā)生變化時通知觀察者。
5). 委托模式:代理+協(xié)議的組合。實現(xiàn)1對1的反向傳值操作。
6). 工廠模式:通過一個類方法,批量的根據(jù)已有模板生產(chǎn)對象。

  • MVC 和 MVVM 的區(qū)別

MVC是一種架構(gòu)模式,M表示Model,V表示視圖View,C表示控制器Controller:
Model負責存儲、定義、操作數(shù)據(jù);
View用來展示給用戶,并且和用戶進行交互;
Controller是Model和View的協(xié)調(diào)者,Controller把Model中的數(shù)據(jù)拿過來給View使用。Controller可以直接與Model和View進行通信,而View不能與Controller直接通信。,當有數(shù)據(jù)更新時,Model也要與Controller進行通信,這個時候就要用Notification和KVO,這個方式就像發(fā)廣播一樣,Model發(fā)信號,Controller設置接收監(jiān)聽信號,當有數(shù)據(jù)更新是就發(fā)信號給Controller,Model和View不能直接通信,這樣違背MVC設計原則。View與Controller通信需要利用代理協(xié)議的方式,Controller可以直接根據(jù)Model決定View的展示。View如果接受響應事件則通過delegate,target-action,block等方式告訴Controller的狀態(tài)變化。Controller進行業(yè)務的處理,然后再控制View的展示。
那這樣Model和View就是相互獨立的。View只負責頁面的展示,Model只是數(shù)據(jù)的存儲,那么也就達到了解耦和重用的目的。
MVVM就是幫忙分擔一下controller里面的部分業(yè)務邏輯。


20160624224107972.png

這個時候,controller將不再直接和真實的model進行綁定了,而通過ViewModel,viewModel進而持有真實的Model。
使用MVVM你會發(fā)現(xiàn)VC里面已經(jīng)省去了不少的代碼。一切都和viewModel進行交流。這里我只是展示一個最簡單的數(shù)據(jù)展示,如果有其他響應事件,是需要viewModel開放方法來進行處理的,并要通知VC處理結(jié)果的。
關(guān)于MVVM的優(yōu)點:
1.方便測試
在MVC下,Controller基本是無法測試的,里面混雜了個各種邏輯,而且分散在不同的地方。有了MVVM我們就可以測試里面的viewModel,來驗證我們的處理結(jié)果對不對(Xcode7的測試已經(jīng)越來越完善了)。
2.便于代碼的移植
比如iOS里面有iPhone版本和iPad版本,除了交互展示不一樣外,業(yè)務邏輯的model是一致的。這樣,我們就可以以很小的代價去開發(fā)另一個app。
3.兼容MVC
MVVM是MVC的一個升級版,目前的MVC也可以很快的轉(zhuǎn)換到MVVM這個模式。VC可以省去一大部分展示邏輯。
缺點:
1.類會增多,每個VC都附帶一個viewModel,類的數(shù)量*2
2.viewModel會越來越龐大
我們把邏輯給了viewModel,那勢必Model也會變得很復雜,里面的屬性和方法越來越多??赡苤貙懙姆椒ū容^多,因為涉及到一些數(shù)據(jù)的轉(zhuǎn)換以及和controller之間的通信。
3.調(diào)用復雜度增加
由于數(shù)據(jù)都是從viewModel來,想想突然來了一個新人,一看代碼,不知道真實的模型是誰。比如常用tableview的數(shù)據(jù)源,一般都是一個數(shù)組,如果不斷的通過viewModel去取,溝通上沒有那么直接。況且每封一層,意味著要寫很多代碼去融合他們的轉(zhuǎn)換。

基礎語法

  • import跟 #include 有什么區(qū)別,@class呢,#import<> 跟 #import””有什么區(qū)別?
    (1)#import指令是Object-C針對@include的改進版本,能確保引用的文件只會被引用一次,不會陷入遞歸包含的問題中;
    (2)@import與@class的區(qū)別:#import會鏈入該頭文件的全部信息,包括實體變量和方法等;二@class只是告訴編譯器,其后面聲明的名稱是類的名稱,至于這些類如何定義的,暫時不用考慮。在頭文件中,一般只需要知道被引用的類的名稱就可以了,不需要知道其內(nèi)部的實體變量和方法,所以在頭文件中一般使用@class來聲明這個名稱是類的名稱;而在實現(xiàn)類里面,因為會用到這個引用類的內(nèi)部的實體變量和方法,所以需要使用#import類包含這個被引用類的頭文件。@class還可以解決循環(huán)包含的問題
    (3)import<>跟#import""的區(qū)別:
    import<>用來包含系統(tǒng)自帶的文件,#import""用來包含自定義的文件

  • 屬性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那種情況下用?
    strong和copy區(qū)別 http://m.itdecent.cn/p/07cff6feace6
    readwrite:是可讀可寫特性,同時生成get方法和set方法的聲明和實現(xiàn)(補充:默認屬性,將生成不帶額外參數(shù)的getter和setter方法(setterff只有一個參數(shù)))
    readonly:只讀特性,只會生成get方法的聲明和實現(xiàn);不希望屬性在類外改變
    assign:是賦值特性,set方法的實現(xiàn)是直接賦值,用于基本數(shù)據(jù)類型;僅設置變量時
    retain:表示持有特性,set方法將傳入?yún)?shù)先保留,再賦值,傳入?yún)?shù)的retaincount會+1;
    copy:表示拷貝特性,set方法的實現(xiàn)是release舊值,copy新值,用于NSString、block等類型(set方法將傳入的對象復制一份;需要完全一份新的變量時使用);
    nonatomic:非原子操作,決定編譯器生成的setter getter是否是原子操作,atomic表示多線程安全,如果寫atomic這個時候生成的setter方法的代碼就會被加上一把線程安全鎖.一般使用nonatomic,代碼中維護鎖

  • week詳解
    Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針。weak表其實是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象的地址)數(shù)組。
    1、初始化時:runtime會調(diào)用objc_initWeak函數(shù),初始化一個新的weak指針指向?qū)ο蟮牡刂贰?br> 2、添加引用時:objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 的作用是更新指針指向,創(chuàng)建對應的弱引用表。
    3、釋放時,調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。

  • Objective-C的類可以多重繼承么?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好?為什么?
    答:Objective-C的類不可以多重繼承;可以實現(xiàn)多個接口(協(xié)議);Category是類別;一般情況用分類好,用Category去重寫類的方法,僅對本Category有效,不會影響到其他類與原有類的關(guān)系。

  • @property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個類中的
    @property 的本質(zhì)是什么?
    @property = ivar + getter + setter;
    “屬性” (property)有兩大概念:ivar(實例變量)、getter+setter(存取方法)
    “屬性” (property)作為 Objective-C 的一項特性,主要的作用就在于封裝對象中的數(shù)據(jù)。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量。實例變量一般通過“存取方法”(access method)來訪問。其中,“獲取方法” (getter)用于讀取變量值,而“設置方法” (setter)用于寫入變量值。

  • Category(類別)、 Extension(擴展)和繼承的區(qū)別
    分類有名字,類擴展沒有分類名字,是一種特殊的分類。
    分類只能擴展方法(屬性僅僅是聲明,并沒真正實現(xiàn)),類擴展可以擴展屬性、成員變量和方法。
    繼承可以增加,修改或者刪除方法,并且可以增加屬性。
    *Extension是Category的一個特例,沒有分類名字,可以擴展屬性,成員變量和方法。常用的擴展是在.m文件中聲明私有屬性和方法,基本上我們天天都在用。

  • KVC的底層實現(xiàn)?
    當一個對象調(diào)用setValue方法時,方法內(nèi)部會做以下操作:
    1). 檢查是否存在相應的key的set方法,如果存在,就調(diào)用set方法。
    2). 如果set方法不存在,就會查找與key相同名稱并且?guī)聞澗€的成員變量,如果有,則直接給成員變量屬性賦值。
    3). 如果沒有找到_key,就會查找相同名稱的屬性key,如果有就直接賦值。
    4). 如果還沒有找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
    這些方法的默認實現(xiàn)都是拋出異常,我們可以根據(jù)需要重寫它們。

  • ViewController生命周期
    按照執(zhí)行順序排列:
    initWithCoder:通過nib文件初始化時觸發(fā)。
    awakeFromNib:nib文件被加載的時候,會發(fā)生一個awakeFromNib的消息到nib文件中的每個對象。
    loadView:開始加載視圖控制器自帶的view。
    viewDidLoad:視圖控制器的view被加載完成。
    viewWillAppear:視圖控制器的view將要顯示在window上。
    updateViewConstraints:視圖控制器的view開始更新AutoLayout約束。
    viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置。
    viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置。
    viewDidAppear:視圖控制器的view已經(jīng)展示到window上。
    viewWillDisappear:視圖控制器的view將要從window上消失。
    viewDidDisappear:視圖控制器的view已經(jīng)從window上消失。

  • OC中的反射機制?簡單聊一下概念和使用
    1). class反射
    通過類名的字符串形式實例化對象。

        Class class = NSClassFromString(@"student"); 
        Student *stu = [[class alloc] init];

將類名變?yōu)樽址?/p>

        Class class =[Student class];
        NSString *className = NSStringFromClass(class);

2). SEL的反射
通過方法的字符串形式實例化方法。
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"];
將方法變成字符串。
NSStringFromSelector(@selector*(setName:));
調(diào)用方法有兩種方式:
利用performSelector 和NSInvocation來調(diào)用
相同點:父類都是NSObject不同點:performSelector最多傳兩個參數(shù),使用比較簡單

  • 內(nèi)存泄漏產(chǎn)生的原因一般是三種情況:
    分配完內(nèi)存之后忘了回收;
    程序Code有問題,造成沒有辦法回收;
    某些API函數(shù)操作不正確,造成內(nèi)存泄漏。

  • 內(nèi)存中的五大區(qū)域及其垃圾回收
    棧: 局部變量. 當局部變量的作用域被執(zhí)行完畢之后,這個局部變量就會被系統(tǒng)立即回收.
    堆: OC對象.使用C函數(shù)申請的空間.
    BSS段: 未初始化的全局變量、靜態(tài)變量. 一旦初始化就回收 并轉(zhuǎn)存到數(shù)據(jù)段之中.
    數(shù)據(jù)段: 已經(jīng)初始化的全局變量、靜態(tài)變量. 直到程序結(jié)束的時候才會被回收.
    代碼段: 代碼. 程序結(jié)束的時候,系統(tǒng)會自動回收存儲在代碼段中的數(shù)據(jù).
    棧、BSS段、數(shù)據(jù)段、代碼段存儲在它們中的數(shù)據(jù)的回收,是由系統(tǒng)自動完成的.不需要我們干預.

  • 懶加載?
    懶加載就是只在用到的時候才去初始化。也可以理解成延時加載。
    我覺得最好也最簡單的一個例子就是tableView中圖片的加載顯示了, 一個延時加載, 避免內(nèi)存過高,一個異步加載,避免線程堵塞提高用戶體驗。

  • 謂詞?
    謂詞就是通過NSPredicate給定的邏輯條件作為約束條件,完成對數(shù)據(jù)的篩選。
    //定義謂詞對象,謂詞對象中包含了過濾條件(過濾條件比較多)

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30];

//使用謂詞條件過濾數(shù)組中的元素,過濾之后返回查詢的結(jié)果

NSArray *array = [persons filteredArrayUsingPredicate:predicate];

isa指針問題

  • isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向?qū)ο蟮念?而Class里也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表。當類方法被調(diào) 用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass)。根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)。
    深入淺出Cocoa之類與對象https://blog.csdn.net/kesalin/article/details/7211228

  • block的注意點
    1). 在block內(nèi)部使用外部指針且會造成循環(huán)引用情況下,需要用__week修飾外部指針:
    __weak typeof(self) weakSelf = self;
    2). 在block內(nèi)部如果調(diào)用了延時函數(shù)還使用弱指針會取不到該指針,因為已經(jīng)被銷毀了,需要在block內(nèi)部再將弱指針重新強引用一下。
    __strong typeof(self) strongSelf = weakSelf;
    3). 如果需要在block內(nèi)部改變外部棧區(qū)變量的話,需要在用__block修飾外部變量。

  • BAD_ACCESS在什么情況下出現(xiàn)?
    這種問題在開發(fā)時經(jīng)常遇到。原因是訪問了野指針,比如訪問已經(jīng)釋放對象的成員變量或者發(fā)消息、死循環(huán)等。

  • Instruments里面工具很多,常用:
    1). Time Profiler: 性能分析
    2). Zombies:檢查是否訪問了僵尸對象,但是這個工具只能從上往下檢查,不智能。
    3). Allocations:用來檢查內(nèi)存,寫算法的那批人也用這個來檢查。(還沒用過)
    4). Leaks:檢查內(nèi)存,看是否有內(nèi)存泄露。

  • GCD 和 NSOperation 都是用于實現(xiàn)多線程:
    GCD 基于C語言的底層API,GCD主要與block結(jié)合使用,代碼簡潔高效。
    NSOperation 屬于Objective-C類,是基于GCD更高一層的封裝。復雜任務一般用NSOperation實現(xiàn)。

  • GCD group
    使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢,就會執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。

// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ }); 
// 當并發(fā)隊列組中的任務執(zhí)行完畢后才會執(zhí)行這里的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并圖片
});
  • GCD 柵欄dispatch_barrier_async
    函數(shù)定義:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
    作用:
    1.在它前面的任務執(zhí)行結(jié)束后它才執(zhí)行,它后面的任務要等它執(zhí)行完成后才會開始執(zhí)行。
    2.避免數(shù)據(jù)競爭
// 1.創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向隊列中添加任務
dispatch_async(queue, ^{  // 1.2是并行的
    NSLog(@"任務1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任務2, %@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"任務 barrier, %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{   // 這兩個是同時執(zhí)行的
    NSLog(@"任務3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任務4, %@",[NSThread currentThread]);
});

// 輸出結(jié)果: 任務1 任務2 ——》 任務 barrier ——》任務3 任務4 
// 其中的任務1與任務2,任務3與任務4 由于是并行處理先后順序不定。
  • OC中創(chuàng)建線程的方法是什么?切換到主線程中執(zhí)行代碼?
// 創(chuàng)建線程的方法
-[NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
-[self performSelectorInBackground:nil withObject:nil];
-[[NSThread alloc] initWithTarget:nil selector:nil object:nil];
-dispatch_async(dispatch_get_global_queue(0, 0), ^{});
-[[NSOperationQueue new] addOperation:nil];
// 主線程中執(zhí)行代碼的方法
-[self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
-dispatch_async(dispatch_get_main_queue(), ^{});
-[[NSOperationQueue mainQueue] addOperation:nil];
  • Runtime
    OC是動態(tài)語言,每個方法在運行時會被動態(tài)轉(zhuǎn)為消息發(fā)送
    Runtime 是OC的底層實現(xiàn),是OC的幕后執(zhí)行者

  • Method Swizzle
    1). 在沒有一個類的實現(xiàn)源碼的情況下,想改變其中一個方法的實現(xiàn),除了繼承它重寫、和借助類別重名方法暴力搶先之外,還有更加靈活的方法 Method Swizzle。
    2). Method Swizzle 指的是改變一個已存在的選擇器對應的實現(xiàn)的過程。OC中方法的調(diào)用能夠在運行時通過改變,通過改變類的調(diào)度表中選擇器到最終函數(shù)間的映射關(guān)系。
    3). 在OC中調(diào)用一個方法,其實是向一個對象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字。利用OC的動態(tài)特性,可以實現(xiàn)在運行時偷換selector對應的方法實現(xiàn)。
    4). 每個類都有一個方法列表,存放著selector的名字和方法實現(xiàn)的映射關(guān)系。IMP有點類似函數(shù)指針,指向具體的方法實現(xiàn)。
    5). 我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP。
    6). 我們可以利用 class_replaceMethod 來修改類。
    7). 我們可以利用 method_setImplementation 來直接設置某個方法的IMP。
    8). 歸根結(jié)底,都是偷換了selector的IMP。

  • _objc_msgForward 函數(shù)是做什么的,直接調(diào)用它將會發(fā)生什么?
    _objc_msgForward是 IMP 類型,用于消息轉(zhuǎn)發(fā)的:當向一個對象發(fā)送一條消息,但它并沒有實現(xiàn)的時候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。

  • 消息轉(zhuǎn)發(fā)


    1829339-b2ed87dd5cd13ea4.png
  • 通信底層原理(OSI七層模型)
    OSI采用了分層的結(jié)構(gòu)化技術(shù),共分七層:
    物理層、數(shù)據(jù)鏈路層、網(wǎng)絡層、傳輸層、會話層、表示層、應用層。

  • XMPP
    XMPP是一種以XML為基礎的開放式實時通信協(xié)議。
    簡單的說,XMPP就是一種協(xié)議,一種規(guī)定。就是說,在網(wǎng)絡上傳東西,XMM就是規(guī)定你上傳大小的格式。

  • tableView的重用機制?
    UITableView 通過重用單元格來達到節(jié)省內(nèi)存的目的: 通過為每個單元格指定一個重用標識符,即指定了單元格的種類,當屏幕上的單元格滑出屏幕時,系統(tǒng)會把這個單元格添加到重用隊列中,等待被重用,當有新單元格從屏幕外滑入屏幕內(nèi)時,從重用隊列中找看有沒有可以重用的單元格,如果有,就拿過來用,如果沒有就創(chuàng)建一個來使用。

  • UIView不規(guī)則點擊事件
    -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

//返回一個view來響應事件 (我們?nèi)绻幌胗绊懴到y(tǒng)的事件傳遞鏈,在這個方法內(nèi),最好調(diào)用父類的這個方法)
- (nullableUIView *)hitTest:(CGPoint)point withEvent:(nullableUIEvent *)event;
//返回的值可以用來判斷是否繼續(xù)遍歷子視圖(返回的根據(jù)是觸摸的point是否在view的frame范圍內(nèi))
- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent *)event;
//重寫該方法后可以讓超出父視圖范圍的子視圖響應事件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *view = [super hitTest:point withEvent:event];
    if (view == nil) {
        for (UIView *subView in self.subviews) {
            CGPoint tp = [subView convertPoint:point fromView:self];
            if (CGRectContainsPoint(subView.bounds, tp)) {
                view = subView;
            }
        }
    }
    return view;
}
  • 響應鏈


    20170317120911452.png
  • 如何實行cell的動態(tài)的行高
    如果希望每條數(shù)據(jù)顯示自身的行高,必須設置兩個屬性,1.預估行高,2.自定義行高。
    設置預估行高 tableView.estimatedRowHeight = 200。
    設置定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。
    如果要讓自定義行高有效,必須讓容器視圖有一個自下而上的約束。

  • main()之前的過程有哪些?
    1)dyld 開始將程序二進制文件初始化
    2)交由ImageLoader 讀取 image,其中包含了我們的類,方法等各種符號(Class、Protocol 、Selector、 IMP)
    3)由于runtime 向dyld 綁定了回調(diào),當image加載到內(nèi)存后,dyld會通知runtime進行處理
    4)runtime 接手后調(diào)用map_images做解析和處理
    5)接下來load_images 中調(diào)用call_load_methods方法,遍歷所有加載進來的Class,按繼承層次依次調(diào)用Class的+load和其他Category的+load方法
    6)至此 所有的信息都被加載到內(nèi)存中
    7)最后dyld調(diào)用真正的main函數(shù)


  • 性能從高到低排序
    1、os_unfair_lock
    2、OSSpinLock
    3、dispatch_semaphore
    4、pthread_mutex
    5、dispatch_queue(DISPATCH_QUEUE_SERIAL)
    6、NSLock
    7、NSCondition
    8、pthread_mutex(recursive)
    9、NSRecursiveLock
    10、NSConditionLock
    11、@synchronized
    詳解:https://juejin.im/post/5bf21d935188251d9e0c2937

  • iOS開發(fā)中靜態(tài)庫和動態(tài)庫區(qū)別:
    靜態(tài)庫和動態(tài)庫是相對編譯期和運行期的:
    靜態(tài)庫在程序編譯時會被鏈接到目標代碼中,程序運行時將不再需要改靜態(tài)庫;而動態(tài)庫在程序編譯時并不會被鏈接到目標代碼中,只是在程序運行時才被載入,因為在程序運行期間還需要動態(tài)庫的存在。

  • 靜態(tài)庫 好處:
    模塊化,分工合作,提高了代碼的復用及核心技術(shù)的保密程度
    避免少量改動經(jīng)常導致大量的重復編譯連接
    也可以重用,注意不是共享使用
  • 動態(tài)庫 好處:
    使用動態(tài)庫,可以將最終可執(zhí)行文件體積縮小,將整個應用程序分模塊,團隊合作,進行分工,影響比較小
    使用動態(tài)庫,多個應用程序共享內(nèi)存中得同一份庫文件,節(jié)省資源
    使用動態(tài)庫,可以不重新編譯連接可執(zhí)行程序的前提下,更新動態(tài)庫文件達到更新應用程序的目的。
    應用插件化
    軟件版本實時模塊升級
  • IOS程序啟動過程
    系統(tǒng)先讀取App的可執(zhí)行文件(Mach-O文件),從里面獲得dyld的路徑,然后加載dyld,dyld去初始化運行環(huán)境,開啟緩存策略,加載程序相關(guān)依賴庫(其中也包含我們的可執(zhí)行文件),并對這些庫進行鏈接,最后調(diào)用每個依賴庫的初始化方法,在這一步,runtime被初始化。當所有依賴庫的初始化后,輪到最后一位(程序可執(zhí)行文件)進行初始化,在這時runtime會對項目中所有類進行類結(jié)構(gòu)初始化,然后調(diào)用所有的load方法。最后dyld返回main函數(shù)地址,main函數(shù)被調(diào)用,我們便來到了熟悉的程序入口。
  • 動態(tài)鏈接庫的加載步驟具體分為5步:
    1.load dylibs image 讀取庫鏡像文件
    2.Rebase image
    3.Bind image
    4.Objc setup
    5.initializers
    詳解:
  • step 1. load dylibs image
    在每個動態(tài)庫的加載過程中, dyld需要:
    1.分析所依賴的動態(tài)庫
    2.找到動態(tài)庫的mach-o文件
    3.打開文件
    4.驗證文件
    5.在系統(tǒng)核心注冊文件簽名
    6.對動態(tài)庫的每一個segment調(diào)用mmap()
    通常的,一個App需要加載100到400個dylibs, 但是其中的系統(tǒng)庫被優(yōu)化,可以很快的加載。
    針對這一步驟的優(yōu)化有:
    1.減少非系統(tǒng)庫的依賴
    2.合并非系統(tǒng)庫
    3.使用靜態(tài)資源,比如把代碼加入主程序
  • step 2.rebase/bind
    由于ASLR(address space layout randomization)的存在,可執(zhí)行文件和動態(tài)鏈接庫在虛擬內(nèi)存中的加載地址每次啟動都不固定,所以需要這2步來修復鏡像中的資源指針,來指向正確的地址。
    rebase修復的是指向當前鏡像內(nèi)部的資源指針;
    而bind指向的是鏡像外部的資源指針。
    rebase步驟先進行,需要把鏡像讀入內(nèi)存,并以page為單位進行加密驗證,保證不會被篡改,所以這一步的瓶頸在IO。
    bind在其后進行,由于要查詢符號表,來指向跨鏡像的資源,加上在rebase階段,鏡像已被讀入和加密驗證,所以這一步的瓶頸在于CPU計算。
    //通過命令行可以查看相關(guān)的資源指針:
    xcrun dyldinfo -rebase -bind -lazy_bind myApp.App/myApp
    優(yōu)化該階段的關(guān)鍵在于減少__DATA segment中的指針數(shù)量。
    可以優(yōu)化的點有:
    1.減少Objc類數(shù)量, 減少selector數(shù)量
    2.減少C++虛函數(shù)數(shù)量
    3.轉(zhuǎn)而使用swift stuct(其實本質(zhì)上就是為了減少符號的數(shù)量)
  • step 3.Objc setup
    這一步主要工作是:
    1.注冊Objc類 (class registration)
    2.把category的定義插入方法列表 (category registration)
    3.保證每一個selector唯一 (selctor uniquing)
    4.由于之前2步驟的優(yōu)化,這一步實際上沒有什么可做的。

  • step 4.initializers
    以上三步屬于靜態(tài)調(diào)整(fix-up),都是在修改__DATA segment中的內(nèi)容,而這里則開始動態(tài)調(diào)整,開始在堆和堆棧中寫入內(nèi)容。 在這里的工作有:
    1.Objc的+load()函數(shù)
    2.C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()
    3.非基本類型的C++靜態(tài)全局變量的創(chuàng)建(通常是類或結(jié)構(gòu)體)(non-trivial initializer) 比如一個全局靜態(tài)結(jié)構(gòu)體的構(gòu)建,如果在構(gòu)造函數(shù)中有繁重的工作,那么會拖慢啟動速度
    Objc的load函數(shù)和C++的靜態(tài)構(gòu)造函數(shù)采用由底向上的方式執(zhí)行,來保證每個執(zhí)行的方法,都可以找到所依賴的動態(tài)庫。
    1).dyld 開始將程序二進制文件初始化
    2).交由 ImageLoader 讀取 image,其中包含了我們的類、方法等各種符號
    3).由于 runtime 向 dyld 綁定了回調(diào),當 image 加載到內(nèi)存后,dyld 會通知 runtime 進行處理
    4).runtime 接手后調(diào)用 mapimages 做解析和處理,接下來 loadimages 中調(diào)用 callloadmethods 方法,遍歷所有加載進來的 Class,按繼承層級依次調(diào)用 Class 的 +load 方法和其 Category 的 +load 方法

  • 至此
    至此,可執(zhí)行文件中和動態(tài)庫所有的符號(Class,Protocol,Selector,IMP,…)都已經(jīng)按格式成功加載到內(nèi)存中,被 runtime 所管理,再這之后,runtime 的那些方法(動態(tài)添加 Class、swizzle 等等才能生效)。
    整個事件由 dyld 主導,完成運行環(huán)境的初始化后,配合 ImageLoader 將二進制文件按格式加載到內(nèi)存, 動態(tài)鏈接依賴庫,并由 runtime 負責加載成 objc 定義的結(jié)構(gòu),所有初始化工作結(jié)束后,dyld 調(diào)用真正的 main 函數(shù)。
    http://m.itdecent.cn/p/7096478ccbe7

  • Autoreleasepool
    根據(jù)蘋果官方文檔中對 NSAutoreleasePool 的描述,我們可知,在主線程的 NSRunLoop 對象(在系統(tǒng)級別的其他線程中應該也是如此,比如通過 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 獲取到的線程)的每個 event loop 開始前,系統(tǒng)會自動創(chuàng)建一個 autoreleasepool ,并在 event loop 結(jié)束時 drain 。我們上面提到的場景 1 中創(chuàng)建的 autoreleased 對象就是被系統(tǒng)添加到了這個自動創(chuàng)建的 autoreleasepool 中,并在這個 autoreleasepool 被 drain 時得到釋放。詳解

持續(xù)化CI

  • fastlane
  • jenkins

組件化

架構(gòu)相關(guān)

第三方

未完待續(xù).....

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容