設(shè)計模式
- 設(shè)計模式
設(shè)計模式是一種編碼經(jīng)驗辩棒,就是用比較成熟的邏輯去處理某一種類型的事情涝影。
1). MVC模式:Model View Control嗤详,把模型 視圖 控制器 層進(jìn)行解耦合編寫礁遣。
2). MVVM模式:Model View ViewModel 把模型 視圖 業(yè)務(wù)邏輯 層進(jìn)行解耦和編寫矾削。
3). 單例模式:通過static關(guān)鍵詞郎逃,聲明全局變量哥童。在整個進(jìn)程運(yù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負(fù)責(zé)存儲忌傻、定義大脉、操作數(shù)據(jù);
View用來展示給用戶水孩,并且和用戶進(jìn)行交互镰矿;
Controller是Model和View的協(xié)調(diào)者,Controller把Model中的數(shù)據(jù)拿過來給View使用俘种。Controller可以直接與Model和View進(jìn)行通信衡怀,而View不能與Controller直接通信。安疗,當(dāng)有數(shù)據(jù)更新時抛杨,Model也要與Controller進(jìn)行通信,這個時候就要用Notification和KVO荐类,這個方式就像發(fā)廣播一樣怖现,Model發(fā)信號,Controller設(shè)置接收監(jiān)聽信號玉罐,當(dāng)有數(shù)據(jù)更新是就發(fā)信號給Controller屈嗤,Model和View不能直接通信,這樣違背MVC設(shè)計原則吊输。View與Controller通信需要利用代理協(xié)議的方式饶号,Controller可以直接根據(jù)Model決定View的展示。View如果接受響應(yīng)事件則通過delegate季蚂,target-action茫船,block等方式告訴Controller的狀態(tài)變化。Controller進(jìn)行業(yè)務(wù)的處理扭屁,然后再控制View的展示算谈。
那這樣Model和View就是相互獨立的。View只負(fù)責(zé)頁面的展示料滥,Model只是數(shù)據(jù)的存儲然眼,那么也就達(dá)到了解耦和重用的目的。
MVVM就是幫忙分擔(dān)一下controller里面的部分業(yè)務(wù)邏輯葵腹。
20160624224107972.png
這個時候高每,controller將不再直接和真實的model進(jìn)行綁定了屿岂,而通過ViewModel,viewModel進(jìn)而持有真實的Model。
使用MVVM你會發(fā)現(xiàn)VC里面已經(jīng)省去了不少的代碼。一切都和viewModel進(jìn)行交流。這里我只是展示一個最簡單的數(shù)據(jù)展示所坯,如果有其他響應(yīng)事件,是需要viewModel開放方法來進(jìn)行處理的霉撵,并要通知VC處理結(jié)果的。
關(guān)于MVVM的優(yōu)點:
1.方便測試
在MVC下洪囤,Controller基本是無法測試的徒坡,里面混雜了個各種邏輯,而且分散在不同的地方瘤缩。有了MVVM我們就可以測試?yán)锩娴膙iewModel喇完,來驗證我們的處理結(jié)果對不對(Xcode7的測試已經(jīng)越來越完善了)。
2.便于代碼的移植
比如iOS里面有iPhone版本和iPad版本剥啤,除了交互展示不一樣外锦溪,業(yè)務(wù)邏輯的model是一致的。這樣府怯,我們就可以以很小的代價去開發(fā)另一個app刻诊。
3.兼容MVC
MVVM是MVC的一個升級版,目前的MVC也可以很快的轉(zhuǎn)換到MVVM這個模式牺丙。VC可以省去一大部分展示邏輯则涯。
缺點:
1.類會增多,每個VC都附帶一個viewModel冲簿,類的數(shù)量*2
2.viewModel會越來越龐大
我們把邏輯給了viewModel粟判,那勢必Model也會變得很復(fù)雜,里面的屬性和方法越來越多峦剔〉到福可能重寫的方法比較多,因為涉及到一些數(shù)據(jù)的轉(zhuǎn)換以及和controller之間的通信吝沫。
3.調(diào)用復(fù)雜度增加
由于數(shù)據(jù)都是從viewModel來呻澜,想想突然來了一個新人,一看代碼野舶,不知道真實的模型是誰易迹。比如常用tableview的數(shù)據(jù)源,一般都是一個數(shù)組平道,如果不斷的通過viewModel去取,溝通上沒有那么直接供炼。況且每封一層一屋,意味著要寫很多代碼去融合他們的轉(zhuǎn)換窘疮。
基礎(chǔ)語法
import跟 #include 有什么區(qū)別,@class呢冀墨,#import<> 跟 #import””有什么區(qū)別闸衫?
(1)#import指令是Object-C針對@include的改進(jìn)版本,能確保引用的文件只會被引用一次诽嘉,不會陷入遞歸包含的問題中蔚出;
(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://www.reibang.com/p/07cff6feace6
readwrite:是可讀可寫特性,同時生成get方法和set方法的聲明和實現(xiàn)(補(bǔ)充:默認(rèn)屬性患蹂,將生成不帶額外參數(shù)的getter和setter方法(setterff只有一個參數(shù)))
readonly:只讀特性或颊,只會生成get方法的聲明和實現(xiàn);不希望屬性在類外改變
assign:是賦值特性传于,set方法的實現(xiàn)是直接賦值囱挑,用于基本數(shù)據(jù)類型;僅設(shè)置變量時
retain:表示持有特性沼溜,set方法將傳入?yún)?shù)先保留平挑,再賦值,傳入?yún)?shù)的retaincount會+1;
copy:表示拷貝特性通熄,set方法的實現(xiàn)是release舊值唆涝,copy新值,用于NSString唇辨、block等類型(set方法將傳入的對象復(fù)制一份廊酣;需要完全一份新的變量時使用);
nonatomic:非原子操作赏枚,決定編譯器生成的setter getter是否是原子操作亡驰,atomic表示多線程安全,如果寫atomic這個時候生成的setter方法的代碼就會被加上一把線程安全鎖.一般使用nonatomic饿幅,代碼中維護(hù)鎖week詳解
Runtime維護(hù)了一個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)建對應(yīng)的弱引用表。
3床绪、釋放時客情,調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組癞己,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為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)用于讀取變量值,而“設(shè)置方法” (setter)用于寫入變量值搔谴。Category(類別)魁袜、 Extension(擴(kuò)展)和繼承的區(qū)別
分類有名字,類擴(kuò)展沒有分類名字敦第,是一種特殊的分類峰弹。
分類只能擴(kuò)展方法(屬性僅僅是聲明,并沒真正實現(xiàn))芜果,類擴(kuò)展可以擴(kuò)展屬性鞠呈、成員變量和方法。
繼承可以增加右钾,修改或者刪除方法蚁吝,并且可以增加屬性。
*Extension是Category的一個特例舀射,沒有分類名字窘茁,可以擴(kuò)展屬性,成員變量和方法。常用的擴(kuò)展是在.m文件中聲明私有屬性和方法脆烟,基本上我們天天都在用山林。KVC的底層實現(xiàn)?
當(dāng)一個對象調(diào)用setValue方法時浩淘,方法內(nèi)部會做以下操作:
1). 檢查是否存在相應(yīng)的key的set方法捌朴,如果存在,就調(diào)用set方法张抄。
2). 如果set方法不存在砂蔽,就會查找與key相同名稱并且?guī)聞澗€的成員變量,如果有署惯,則直接給成員變量屬性賦值左驾。
3). 如果沒有找到_key,就會查找相同名稱的屬性key,如果有就直接賦值诡右。
4). 如果還沒有找到安岂,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
這些方法的默認(rèn)實現(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中的反射機(jī)制?簡單聊一下概念和使用
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ū)域及其垃圾回收
棧: 局部變量. 當(dāng)局部變量的作用域被執(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)自動完成的.不需要我們干預(yù).懶加載?
懶加載就是只在用到的時候才去初始化慧库。也可以理解成延時加載跷跪。
我覺得最好也最簡單的一個例子就是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(元類)葛菇。元類保存了類方法的列表。當(dāng)類方法被調(diào) 用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法橡羞。同時注意的是:元類(meteClass)也是類,它也是對象眯停。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass)。根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)卿泽。
深入淺出Cocoa之類與對象https://blog.csdn.net/kesalin/article/details/7211228block的注意點
1). 在block內(nèi)部使用外部指針且會造成循環(huán)引用情況下莺债,需要用__week修飾外部指針:
__weak typeof(self) weakSelf = self;
2). 在block內(nèi)部如果調(diào)用了延時函數(shù)還使用弱指針會取不到該指針,因為已經(jīng)被銷毀了又厉,需要在block內(nèi)部再將弱指針重新強(qiáng)引用一下九府。
__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更高一層的封裝尺迂。復(fù)雜任務(wù)一般用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 */ });
// 當(dāng)并發(fā)隊列組中的任務(wù)執(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.在它前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,它后面的任務(wù)要等它執(zhí)行完成后才會開始執(zhí)行股毫。
2.避免數(shù)據(jù)競爭
// 1.創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向隊列中添加任務(wù)
dispatch_async(queue, ^{ // 1.2是并行的
NSLog(@"任務(wù)1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2, %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"任務(wù) barrier, %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{ // 這兩個是同時執(zhí)行的
NSLog(@"任務(wù)3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4, %@",[NSThread currentThread]);
});
// 輸出結(jié)果: 任務(wù)1 任務(wù)2 ——》 任務(wù) barrier ——》任務(wù)3 任務(wù)4
// 其中的任務(wù)1與任務(wù)2膳音,任務(wù)3與任務(wù)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)語言薄嫡,每個方法在運(yùn)行時會被動態(tài)轉(zhuǎn)為消息發(fā)送
Runtime 是OC的底層實現(xiàn),是OC的幕后執(zhí)行者Method Swizzle
1). 在沒有一個類的實現(xiàn)源碼的情況下颗胡,想改變其中一個方法的實現(xiàn)毫深,除了繼承它重寫、和借助類別重名方法暴力搶先之外毒姨,還有更加靈活的方法 Method Swizzle哑蔫。
2). Method Swizzle 指的是改變一個已存在的選擇器對應(yīng)的實現(xiàn)的過程。OC中方法的調(diào)用能夠在運(yùn)行時通過改變弧呐,通過改變類的調(diào)度表中選擇器到最終函數(shù)間的映射關(guān)系闸迷。
3). 在OC中調(diào)用一個方法,其實是向一個對象發(fā)送消息俘枫,查找消息的唯一依據(jù)是selector的名字腥沽。利用OC的動態(tài)特性,可以實現(xiàn)在運(yùn)行時偷換selector對應(yīng)的方法實現(xiàn)鸠蚪。
4). 每個類都有一個方法列表今阳,存放著selector的名字和方法實現(xiàn)的映射關(guān)系。IMP有點類似函數(shù)指針茅信,指向具體的方法實現(xiàn)盾舌。
5). 我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP。
6). 我們可以利用 class_replaceMethod 來修改類蘸鲸。
7). 我們可以利用 method_setImplementation 來直接設(shè)置某個方法的IMP妖谴。
8). 歸根結(jié)底,都是偷換了selector的IMP酌摇。_objc_msgForward 函數(shù)是做什么的膝舅,直接調(diào)用它將會發(fā)生什么?
_objc_msgForward是 IMP 類型妙痹,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個對象發(fā)送一條消息铸史,但它并沒有實現(xiàn)的時候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)怯伊。-
消息轉(zhuǎn)發(fā)
1829339-b2ed87dd5cd13ea4.png 通信底層原理(OSI七層模型)
OSI采用了分層的結(jié)構(gòu)化技術(shù)琳轿,共分七層:
物理層、數(shù)據(jù)鏈路層耿芹、網(wǎng)絡(luò)層崭篡、傳輸層、會話層吧秕、表示層琉闪、應(yīng)用層。XMPP
XMPP是一種以XML為基礎(chǔ)的開放式實時通信協(xié)議砸彬。
簡單的說颠毙,XMPP就是一種協(xié)議斯入,一種規(guī)定。就是說蛀蜜,在網(wǎng)絡(luò)上傳東西刻两,XMM就是規(guī)定你上傳大小的格式。tableView的重用機(jī)制滴某?
UITableView 通過重用單元格來達(dá)到節(jié)省內(nèi)存的目的: 通過為每個單元格指定一個重用標(biāo)識符磅摹,即指定了單元格的種類,當(dāng)屏幕上的單元格滑出屏幕時,系統(tǒng)會把這個單元格添加到重用隊列中霎奢,等待被重用户誓,當(dāng)有新單元格從屏幕外滑入屏幕內(nèi)時,從重用隊列中找看有沒有可以重用的單元格幕侠,如果有帝美,就拿過來用,如果沒有就創(chuàng)建一個來使用橙依。UIView不規(guī)則點擊事件
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
//返回一個view來響應(yīng)事件 (我們?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;
//重寫該方法后可以讓超出父視圖范圍的子視圖響應(yīng)事件
- (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;
}
-
響應(yīng)鏈
20170317120911452.png 如何實行cell的動態(tài)的行高
如果希望每條數(shù)據(jù)顯示自身的行高漆枚,必須設(shè)置兩個屬性创译,1.預(yù)估行高,2.自定義行高墙基。
設(shè)置預(yù)估行高 tableView.estimatedRowHeight = 200软族。
設(shè)置定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。
如果要讓自定義行高有效残制,必須讓容器視圖有一個自下而上的約束立砸。main()之前的過程有哪些?
1)dyld 開始將程序二進(jìn)制文件初始化
2)交由ImageLoader 讀取 image初茶,其中包含了我們的類颗祝,方法等各種符號(Class、Protocol 恼布、Selector螺戳、 IMP)
3)由于runtime 向dyld 綁定了回調(diào),當(dāng)image加載到內(nèi)存后折汞,dyld會通知runtime進(jìn)行處理
4)runtime 接手后調(diào)用map_images做解析和處理
5)接下來load_images 中調(diào)用call_load_methods方法倔幼,遍歷所有加載進(jìn)來的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/5bf21d935188251d9e0c2937iOS開發(fā)中靜態(tài)庫和動態(tài)庫區(qū)別:
靜態(tài)庫和動態(tài)庫是相對編譯期和運(yùn)行期的:
靜態(tài)庫在程序編譯時會被鏈接到目標(biāo)代碼中而咆,程序運(yùn)行時將不再需要改靜態(tài)庫;而動態(tài)庫在程序編譯時并不會被鏈接到目標(biāo)代碼中幕袱,只是在程序運(yùn)行時才被載入暴备,因為在程序運(yùn)行期間還需要動態(tài)庫的存在。
- 靜態(tài)庫 好處:
模塊化们豌,分工合作涯捻,提高了代碼的復(fù)用及核心技術(shù)的保密程度
避免少量改動經(jīng)常導(dǎo)致大量的重復(fù)編譯連接
也可以重用,注意不是共享使用 - 動態(tài)庫 好處:
使用動態(tài)庫望迎,可以將最終可執(zhí)行文件體積縮小障癌,將整個應(yīng)用程序分模塊,團(tuán)隊合作辩尊,進(jìn)行分工涛浙,影響比較小
使用動態(tài)庫,多個應(yīng)用程序共享內(nèi)存中得同一份庫文件摄欲,節(jié)省資源
使用動態(tài)庫轿亮,可以不重新編譯連接可執(zhí)行程序的前提下,更新動態(tài)庫文件達(dá)到更新應(yīng)用程序的目的胸墙。
應(yīng)用插件化
軟件版本實時模塊升級
- IOS程序啟動過程
系統(tǒng)先讀取App的可執(zhí)行文件(Mach-O文件)我注,從里面獲得dyld的路徑,然后加載dyld迟隅,dyld去初始化運(yùn)行環(huán)境但骨,開啟緩存策略,加載程序相關(guān)依賴庫(其中也包含我們的可執(zhí)行文件)玻淑,并對這些庫進(jìn)行鏈接嗽冒,最后調(diào)用每個依賴庫的初始化方法,在這一步补履,runtime被初始化添坊。當(dāng)所有依賴庫的初始化后,輪到最后一位(程序可執(zhí)行文件)進(jìn)行初始化箫锤,在這時runtime會對項目中所有類進(jìn)行類結(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步來修復(fù)鏡像中的資源指針赠摇,來指向正確的地址。
rebase修復(fù)的是指向當(dāng)前鏡像內(nèi)部的資源指針浅蚪;
而bind指向的是鏡像外部的資源指針藕帜。
rebase步驟先進(jìn)行,需要把鏡像讀入內(nèi)存惜傲,并以page為單位進(jìn)行加密驗證洽故,保證不會被篡改,所以這一步的瓶頸在IO操漠。
bind在其后進(jìn)行收津,由于要查詢符號表,來指向跨鏡像的資源浊伙,加上在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.注冊O(shè)bjc類 (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 開始將程序二進(jìn)制文件初始化
2).交由 ImageLoader 讀取 image,其中包含了我們的類愤炸、方法等各種符號
3).由于 runtime 向 dyld 綁定了回調(diào)期揪,當(dāng) image 加載到內(nèi)存后,dyld 會通知 runtime 進(jìn)行處理
4).runtime 接手后調(diào)用 mapimages 做解析和處理规个,接下來 loadimages 中調(diào)用 callloadmethods 方法凤薛,遍歷所有加載進(jìn)來的 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 主導(dǎo)珍剑,完成運(yùn)行環(huán)境的初始化后,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存死陆, 動態(tài)鏈接依賴庫招拙,并由 runtime 負(fù)責(zé)加載成 objc 定義的結(jié)構(gòu),所有初始化工作結(jié)束后措译,dyld 調(diào)用真正的 main 函數(shù)别凤。
http://www.reibang.com/p/7096478ccbe7Autoreleasepool
根據(jù)蘋果官方文檔中對 NSAutoreleasePool 的描述,我們可知领虹,在主線程的 NSRunLoop 對象(在系統(tǒng)級別的其他線程中應(yī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ù).....