1,禁止init初始化
開發(fā)中,如果想指定初始化方法,不允許調(diào)用者亂用,可以用這兩個宏:
NS_DESIGNATED_INITIALIZER,(- (instancetype)init NS_DESIGNATED_INITIALIZER;)
NS_UNAVAILABLE (- (instancetype)init NS_UNAVAILABLE;)
2,self.xx和_xx 區(qū)別
self.xx是調(diào)用的xx屬性的get/set方法,而_xx則只是使用成員變量_xx,并不會調(diào)用get/set方法。兩者的更深層次的區(qū)別在于,通過存取方法訪問比直接訪問多做了一些其他的事情(例如內(nèi)存管理,復(fù)制值等),例如如果屬性在@property中屬性的修飾符有retain,那么當(dāng)使用self.xx的時候相應(yīng)的屬性的引用計數(shù)器由于生成了setter方法而進(jìn)行加1操作,此時的retaincount為2。
3,block本質(zhì)上是什么,如何避免循環(huán)引用
- block本質(zhì)上也是一個OC對象,它內(nèi)部也有個isa指針,由6部分構(gòu)成
- isa 指針,所有對象都有該指針,用于實(shí)現(xiàn)對象相關(guān)的功能。
- flags,用于按 bit 位表示一些 block 的附加信息,
- reserved,保留變量。
- invoke,函數(shù)指針,指向具體的 block 實(shí)現(xiàn)的函數(shù)調(diào)用地址。
- descriptor, 表示該 block 的附加描述信息
- variables,capture 過來的變量,block 能夠訪問它外部的局部變量,就是因?yàn)閷⑦@些變量(或變量的地址)復(fù)制到了結(jié)構(gòu)體中
- block類型 (全局,棧,堆)默認(rèn)創(chuàng)建的是棧,需要copy到堆上
- block 循環(huán)引用,首先需要分析哪些操作會產(chǎn)生循環(huán)引用,一般是堆上的block,(self->block->self) 打破需要加入__weak 機(jī)制(self->weakSelf weakSelf->block->self)
4,copy修飾符的作用
拷貝一份不可變的副本,屬性值不受外部變量變化而變化,相應(yīng)的修飾可變對象時,會導(dǎo)致調(diào)用增刪改的方式崩潰
5,lldb 如何觀察kvo
watchpoint
iOS開發(fā)當(dāng)中有一個重要的概念KVO,我們會給一個重要的變量設(shè)置一個觀察者,用以在它發(fā)生變化的時候做出相應(yīng)的操作。在調(diào)試過程中我們也可以借助LLDB來監(jiān)視某個變量或某一塊內(nèi)存的讀寫情況。
我們利用watchpoint指令來監(jiān)視變量情況。
p obj->isa 可以在set過程觀察obj isa指針的變化
6,常見的鎖有哪些
7,設(shè)計模式
開閉原則是總綱,它告訴我們要對擴(kuò)展開放,對修改關(guān)閉;
里氏替換原則告訴我們不要破壞繼承體系;
依賴倒置原則告訴我們要面向接口編程;
單一職責(zé)原則告訴我們實(shí)現(xiàn)類要職責(zé)單一;
接口隔離原則告訴我們在設(shè)計接口的時候要精簡單一;
迪米特法則告訴我們要降低耦合度;
合成復(fù)用原則告訴我們要優(yōu)先使用組合或者聚合關(guān)系復(fù)用,少用繼承關(guān)系復(fù)用。
8,常用的多線程管理,GCD如何實(shí)現(xiàn)線程的依賴和返回
- NSThead
- GCD
- NSOperationQueue
9,cocopods PodSpecs 所用 install和update區(qū)別
pod install的話,只會處理沒有記錄在Podfile.lock中的依賴庫,會查找匹配Podfile中描述的版本。對于已經(jīng)記錄在Podfile.lock的依賴庫,會下載Podfile.lock文件中記錄的版本,而不會檢查是否有更新。當(dāng)然,如果你約束了pods的版本的話,會按照你指定的版本進(jìn)行安裝,同時也會更新Podfile.lock記錄的信息。
pod update 要更新庫的名字
這個命令會忽略Podfile.lock中的記錄,直接去找符合Podfile文件中的該依賴庫 的約束版本(無約束的話就是最新版本)。
10,Runloop與performSelector
指定延時0 并不會立即執(zhí)行選擇器,而是在線程循環(huán)運(yùn)行中排隊(duì),并盡快執(zhí)行
當(dāng)調(diào)用 NSObject 的 performSelecter:afterDelay: 后,實(shí)際上其內(nèi)部會創(chuàng)建一個 Timer 并添加到當(dāng)前線程的 RunLoop 中。所以如果當(dāng)前線程沒有 RunLoop,則這個方法會失效。
當(dāng)調(diào)用 performSelector:onThread: 時,實(shí)際上其會創(chuàng)建一個 Timer 加到對應(yīng)的線程去,同樣的,如果對應(yīng)線程沒有 RunLoop 該方法也會失效。
11
沙盒結(jié)構(gòu)
1.document:
應(yīng)用程序在運(yùn)行時生成的一些需要長久保存的重要數(shù)據(jù)放在此文件中。通過iTunes,iCloud備份時,會備份此目錄下的數(shù)據(jù)。iTunes共享文件時,可以共享此文件目錄。
2.Library:
a.Cache:存放緩存文件,比如從網(wǎng)路上下載的數(shù)據(jù)或數(shù)據(jù)。一般用來保存應(yīng)用需要長期使用的,數(shù)據(jù)量大,不需要備份的非重要數(shù)據(jù)。iTunes,iCloud備份時不會備份此目錄下的數(shù)據(jù)
b.Prefrence:保存應(yīng)用的所有偏好設(shè)置,比如賬號,設(shè)置等。由系統(tǒng)自動管理。iTunes,iCloud備份時會備份此目錄下的數(shù)據(jù)和文件(NSUerdefault 就在保存在這里的一個plist上)
3.tmp:
用于保存應(yīng)用在運(yùn)行時產(chǎn)生的一些臨時數(shù)據(jù)文件,手機(jī)重啟,系統(tǒng)空間不足的,關(guān)閉應(yīng)用等場景下可能會刪除該文件下的文件。iTunes備份時不會備份該目錄下的文件
12,交替打印線程
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
NSCondition *lock = [NSCondition new];
dispatch_async(queue, ^{
for (NSInteger i = 0; i<100; i++) {
[lock lock];
if (i%2 == 0) {
NSLog(@"偶數(shù)%zd,%@",i,[NSThread currentThread]);
[lock signal];
[lock wait];
}
[lock unlock];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<100; i++) {
[lock lock];
if (i%2 != 0) {
NSLog(@"奇數(shù)%zd,%@",i,[NSThread currentThread]);
[lock signal];
[lock wait];
}
[lock unlock];
}
});
13,鏈表的反轉(zhuǎn)(非遞歸和遞歸)
- (Node *) reverseList(Node*) {
ListNode root = head;
ListNode pre = null;
ListNode next = null;
if(head == null){
return null;
}
while(root.next != null){
next = root.next;
root.next = pre;
pre = root;
root = next;
}
root.next = pre;
return root;
}
//遞歸
- (Node *) reverseList(Node*) {
if(head == null || head.next == null){
return head;
}else{
ListNode newhead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newhead;
}
}
14,判斷自定義對象重復(fù)
可參考文章自定義對象重復(fù)
isEqual與hash
15,iOS Autolayout
intrinsicContentSize : 固有大小。顧名思義,在AutoLayout中,它作為UIView的屬性,意思就是說如果你沒有為我指定大小,我就按照這個大小來。
像UILabel,UIImageView,UIButton等這些組件及某些包含它們的系統(tǒng)組件都有 Intrinsic Content Size 屬性,比如:當(dāng)我們在給UILabel使用AutoLayout的時候,不用指定尺寸大小的,只需指定位置即可,就是因?yàn)?,只要確定了文字內(nèi)容,字體等信息,它自己就能計算出大小來。
content Hugging Priority:直譯成中文就是“內(nèi)容擁抱優(yōu)先級”,從字面意思上來看就是兩個視圖,誰的“內(nèi)容擁抱優(yōu)先級”高,誰就優(yōu)先環(huán)繞其內(nèi)容。將白了就是確定view有多大的優(yōu)先級阻止自己變大。更通俗易懂的理解就是:如果組件的此屬性優(yōu)先級比另一個組件此屬性優(yōu)先級高的話,那么這個組件就保持不變,另一個可以在需要拉伸的時候拉伸。
content Compression Resistance Priority:該優(yōu)先級直譯成中文就是“內(nèi)容壓縮阻力優(yōu)先級”。也就是視圖的“內(nèi)容壓縮阻力優(yōu)先級”越大,那么該視圖中的內(nèi)容越難被壓縮。而該優(yōu)先級小的視圖,則內(nèi)容優(yōu)先被壓縮。將白了就是確定有多大的優(yōu)先級阻止自己變小。更通俗易懂的理解就是:如果組件的此屬性優(yōu)先級比另一個組件此屬性優(yōu)先級高的話,那么這個組件就保持不變,另一個可以在需要壓縮的時候壓縮。
16,紅黑樹
紅黑樹 是一種自平衡的二叉查找樹
二叉查找樹(BST)具備什么特性呢?
1.左子樹上所有結(jié)點(diǎn)的值均小于或等于它的根結(jié)點(diǎn)的值。
2.右子樹上所有結(jié)點(diǎn)的值均大于或等于它的根結(jié)點(diǎn)的值。
3.左、右子樹也分別為二叉排序樹。
紅黑樹的特點(diǎn)
1.節(jié)點(diǎn)是紅色或黑色。
2.根節(jié)點(diǎn)是黑色。
3.每個葉子節(jié)點(diǎn)都是黑色的空節(jié)點(diǎn)(NIL節(jié)點(diǎn))。
4 每個紅色節(jié)點(diǎn)的兩個子節(jié)點(diǎn)都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續(xù)的紅色節(jié)點(diǎn))
5.從任一節(jié)點(diǎn)到其每個葉子的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。
可參考文章紅黑樹
17,iOS 啟動過程
可分為 main之前 和main 函數(shù)之后,主要寫main函數(shù)之前
- 加載可執(zhí)行文件(App 的.o文件的集合);
幾乎所有 Mach-O 都包含這三個段(segment): __TEXT,__DATA和 __LINKEDIT:
__TEXT
包含 Mach header,被執(zhí)行的代碼和只讀常量(如C 字符串)。只讀可執(zhí)行(rx-)。
__DATA
包含全局變量,靜態(tài)變量等??勺x寫(rw-)。
__LINKEDIT
包含了加載程序的『元數(shù)據(jù)』,比如函數(shù)的名稱和地址。只讀(r–)。
Mach-O Universal文件
FAT 二進(jìn)制文件,將多種架構(gòu)的Mach-O文件合并而成。它通過 Fat Header 來記錄不同架構(gòu)在文件中的偏移量,F(xiàn)at Header 占一頁的空間。
按分頁來存儲這些segement和 header會浪費(fèi)空間,但這有利于虛擬內(nèi)存的實(shí)現(xiàn)。
- 加載動態(tài)鏈接庫,進(jìn)行 rebase 指針調(diào)整和 bind 符號綁定;
Load dylibs -> Rebase -> Bind -> ObjC -> Initializers
- Objc 運(yùn)行時的初始處理,包括 Objc 相關(guān)類的注冊、category 注冊、selector 唯一性檢查等;
Objective-C 中有很多數(shù)據(jù)結(jié)構(gòu)都是靠 Rebasing 和 Binding來(fix-up)的,比如 Class 中指向元類的指針和指向方法的指針。
ObjC是個動態(tài)語言,可以用類的名字來實(shí)例化一個類的對象。這意味著 ObjC Runtime 需要維護(hù)一張映射類名與類的全局表。當(dāng)加載一個 dylib 時,其定義的所有的類都需要被注冊到這個全局表中。
C++ 中有個問題叫做易碎的基類(fragile base class)。ObjC 就沒有這個問題,因?yàn)闀诩虞d時通過fix-up動態(tài)類中改變實(shí)例變量的偏移量。
- 初始化,包括了執(zhí)行 +load() 方法、attribute((constructor)) 修飾的函數(shù)的調(diào)用、創(chuàng)建 C++ 靜態(tài)全局變量;
- 會分別執(zhí)行每個類和分類的+load方法
- attribute((constructor))修飾的函數(shù)在main函數(shù)之前執(zhí)行 ,attribute((destructor))修飾的函數(shù)在main函數(shù)之后執(zhí)行
- C++ 靜態(tài)全局變量在類加載之前被初始化,限于類中使用
啟動時間優(yōu)化
1 減少動態(tài)庫加載。每個庫本身都有依賴關(guān)系,蘋果公司建議使用更少的動態(tài)庫。靜態(tài)庫的加載時間更短,但是當(dāng)我們的Extension和App需要使用同一部分代碼時,我們需要將其封裝動態(tài)庫 (See this blog)。動態(tài)庫同時也能解決二進(jìn)制包文件過大的問題,蘋果對app包大小判斷是不將動態(tài)庫包大小計算在內(nèi)的。
2 check framework應(yīng)設(shè)為optional和required,如果該framework在當(dāng)前App支持的所有iOS系統(tǒng)版本都存在,那么就設(shè)為required,否則就設(shè)為optional,因?yàn)閛ptional會有些額外的檢查;
3 減少加載啟動后不會去使用的類或者方法。
4 合并或者刪減一些OC類,關(guān)于清理項(xiàng)目中沒用到的類,可以借助AppCode代碼檢查工具:
5 刪減一些無用的靜態(tài)變量
6 刪減沒有被調(diào)用到或者已經(jīng)廢棄的方法
7 盡量不要用C++虛函數(shù)(創(chuàng)建虛函數(shù)表有開銷)
8 避免使用 attribute((constructor)),可將要實(shí)現(xiàn)的內(nèi)容放在初始化方法中配合 dispatch_once 使用。
9 +load()方法里的內(nèi)容可以放到首屏渲染完成后再執(zhí)行,或使用 +initialize()方法替換掉。因?yàn)?,在一個 +load() 方法里,進(jìn)行運(yùn)行時方法替換操作會帶來 4 毫秒的消耗。不要小看這4 毫秒,積少成多,執(zhí)行+load()方法對啟動速度的影響會越來越大。
10 控制C++ 全局變量的數(shù)量。
二進(jìn)制重排
二進(jìn)制重排是虛擬內(nèi)存基于頁和段來加載讀取的原理,抖音團(tuán)隊(duì)對二進(jìn)制重排有一篇文章進(jìn)行了講解
可參考文章iOS啟動