iOS 基礎(chǔ)知識(shí)(面試)深度理解-- 不斷更新

什么是KVC,什么是KVO,他們之間關(guān)系.底層實(shí)現(xiàn)

鍵值編碼 Key-Value-Coding(KVC)

鍵值編碼是一種使用字符串來(lái)標(biāo)識(shí)屬性,間接訪問(wèn)對(duì)象的屬性,而不是通過(guò)調(diào)用存取方法,直接或通過(guò)實(shí)例變量訪問(wèn)的機(jī)制,非對(duì)象類型的變量將被自動(dòng)封裝或者解封成對(duì)象,很多情況下會(huì)簡(jiǎn)化程序代碼;KVC的缺點(diǎn):一旦使用 KVC 你的編譯器無(wú)法檢查出錯(cuò)誤,即不會(huì)對(duì)設(shè)置的鍵、鍵路徑進(jìn)行錯(cuò)誤檢查,且執(zhí)行效率要低于合成存取器方法和自定的 setter 和 getter 方法。因?yàn)槭褂?KVC 鍵值編碼,它必須先解析字符串,然后在設(shè)置或者訪問(wèn)對(duì)象的實(shí)例變量。

實(shí)現(xiàn)分析

KVC運(yùn)用了一個(gè)isa-swizzling技術(shù)。isa-swizzling就是類型混合指針機(jī)制。KVC主要通過(guò)isa-swizzling,來(lái)實(shí)現(xiàn)其內(nèi)部查找定位的。isa指針,如其名稱所指,(就是is a kind of的意思),指向維護(hù)分發(fā)表的對(duì)象的類。該分發(fā)表實(shí)際上包含了指向?qū)崿F(xiàn)類中的方法的指針,和其它數(shù)據(jù)。

比如說(shuō)如下的一行KVC的代碼:
[site setValue:@"sitename" forKey:@"name"];
就會(huì)被編譯器處理成:
SEL sel = sel_get_uid ("setValue:forKey:");
IMP method = objc_msg_lookup (site->isa,sel);
method(site, sel, @"sitename", @“name");

一個(gè)對(duì)象在調(diào)用setValue的時(shí)候,
(1)首先根據(jù)方法名找到運(yùn)行方法的時(shí)候所需要的環(huán)境參數(shù)。
(2)他會(huì)從自己isa指針結(jié)合環(huán)境參數(shù),找到具體的方法實(shí)現(xiàn)的接口。
(3)再直接查找得來(lái)的具體的方法實(shí)現(xiàn)。

鍵值觀察 Key-Value Observing (KVO)

鍵值觀察機(jī)制是一種能使得對(duì)象獲取到其他對(duì)象屬性變化的通知 ,極大的簡(jiǎn)化了代碼。

實(shí)現(xiàn)分析 :
KVO的實(shí)現(xiàn)是基于runtime運(yùn)行時(shí)的
1、當(dāng)一個(gè)object有觀察者時(shí),動(dòng)態(tài)創(chuàng)建這個(gè)object的類的子類在addObserver:forKeyPath:options:context:之后。對(duì)象的isa變?yōu)榱俗宇惻缮怤SKVONotifying_XX。實(shí)際上是對(duì)象p的isa即NSKVONotifying_XX類的setAge方法,并非原類的setAge方法。
2、對(duì)于每個(gè)被觀察的property,重寫其set方法
3、在重寫的set方法中調(diào)用- willChangeValueForKey:和- didChangeValueForKey:通知觀察者
4、當(dāng)一個(gè)property沒(méi)有觀察者時(shí),刪除重寫的方法
5、當(dāng)沒(méi)有observer觀察任何一個(gè)property時(shí),刪除動(dòng)態(tài)創(chuàng)建的子類

什么是block,delegate,通知中心Notification,使用區(qū)別

block
block被ObjC看成是對(duì)象,它封裝了一段代碼,這段代碼可以在任何時(shí)候執(zhí)行。Blocks可以作為函數(shù)參數(shù)或者函數(shù)的返回值,而其本身又可以帶輸入?yún)?shù)或返回值。它和傳統(tǒng)的函數(shù)指針很類似,但是有區(qū)別:blocks是inline的,并且它對(duì)局部變量是只讀的。block類似一些其它Web編程語(yǔ)言中的“匿名函數(shù)”。在objc中通常使用block實(shí)現(xiàn)代理方法實(shí)現(xiàn)的功能,也就是回調(diào)。使用代理需要設(shè)置代理的數(shù)據(jù)接收者,而且代理方法是被分離開(kāi)來(lái)處理的,block可以將這些分離的代碼放到一個(gè)代碼塊中。

delegate
delegate,又稱委托或者代理,它是一種設(shè)計(jì)模式.delegate主要是用于兩個(gè)對(duì)象之間的交互,并且解除兩個(gè)通信對(duì)象的耦合性,iOS大量使用代理模式,主要是用于視圖與使用對(duì)象之間的通信交互.

官方文檔翻譯解釋:
代理是一種簡(jiǎn)單而功能強(qiáng)大的設(shè)計(jì)模式,這種模式用于一個(gè)對(duì)象“代表”另外一個(gè)對(duì)象和程序中其他的對(duì)象進(jìn)行交互。 主對(duì)象(這里指的是delegating object)中維護(hù)一個(gè)代理(delegate)的引用并且在合適的時(shí)候向這個(gè)代理發(fā)送消息。這個(gè)消息通知“代理”主對(duì)象即將處理或是已經(jīng)處理完了某一個(gè)事件。這個(gè)代理可以通過(guò)更新自己或是其它對(duì)象的UI界面或是其它狀態(tài)來(lái)響應(yīng)主對(duì)象所發(fā)送過(guò)來(lái)的這個(gè)事件的消息?;蚴窃谀承┣闆r下能返回一個(gè)值來(lái)影響其它即將發(fā)生的事件該如何來(lái)處理。代理的主要價(jià)值是它可以讓你容易的定制各種對(duì)象的行為。注意這里的代理是個(gè)名詞,它本身是一個(gè)對(duì)象,這個(gè)對(duì)象是專門代表被代理對(duì)象來(lái)和程序中其他對(duì)象打交道的。委托是objC中使用非常頻繁的一種設(shè)計(jì)模式,它的實(shí)現(xiàn)與協(xié)議的使用是分不開(kāi)的.

Notification
通知中心概述:通知中心實(shí)際上是在程序內(nèi)部提供了消息廣播的一種機(jī)制。通知中心不能在進(jìn)程間進(jìn)行通信。實(shí)際上就是一個(gè)二傳手,把接收到的消息,根據(jù)內(nèi)部的一個(gè)消息轉(zhuǎn)發(fā)表,來(lái)將消息轉(zhuǎn)發(fā)給需要的對(duì)象。通知中心是基于觀察者模式的,它允許注冊(cè)、刪除觀察者。
一個(gè)NSNotificationCenter可以有許多的通知消息NSNotification,對(duì)于每一個(gè)NSNotification可以有很多的觀察者Observer來(lái)接收通知。

使用區(qū)別

delegate與block一般用于兩個(gè)對(duì)象1對(duì)1之間的通信交互、delegate需要定義協(xié)議方法,代理對(duì)象需要實(shí)現(xiàn)協(xié)議方法并且需要建立代理關(guān)系才可以實(shí)現(xiàn)通信。 block更加簡(jiǎn)潔,不需要定義繁瑣的協(xié)議方法,但是如果通信時(shí)間比較多的話,建議使用delgate。 Notfication主要用于1對(duì)多的通信,而且通信對(duì)象之間不需要建立關(guān)系,但是使用通知,代碼的可讀性差。

內(nèi)存管理MRC、ARC管理機(jī)制的區(qū)別

iOS中采用的是引用計(jì)數(shù)的機(jī)制來(lái)管理內(nèi)存,如果一塊內(nèi)存區(qū)域的引用計(jì)數(shù)不為0,那么就說(shuō)明有對(duì)象持或者是在使用這一塊內(nèi)存,如果引用計(jì)數(shù)為0的話那么說(shuō)明這塊內(nèi)存沒(méi)有對(duì)象使用,可以被系統(tǒng)回收掉。iOS借助于引用計(jì)數(shù)的增減來(lái)輔助我們進(jìn)行內(nèi)存的申請(qǐng)和釋放.

內(nèi)存管理的原則:
1、自己創(chuàng)建的對(duì)象,自己可以持有(比如以alloc、new、copy、mutableCopy開(kāi)頭的方法可以創(chuàng)建對(duì)象);
2、不是自己創(chuàng)建的對(duì)象也可以持有,通過(guò)retain;
3、自己持有的對(duì)象在不需要使用的時(shí)候要負(fù)責(zé)釋放、釋放可以通過(guò)release或者是autorelease進(jìn)行釋放,
4、不是自己持有的對(duì)象不能進(jìn)行釋放,比如便利構(gòu)造器得到的對(duì)象。

在內(nèi)存管理的過(guò)程中我們需要謹(jǐn)記的原則就是我們?cè)斐傻囊糜?jì)數(shù)的增加和我們?cè)斐傻囊糜?jì)數(shù)的減少要保持一致。在我們使用屬性的過(guò)程中,要注意不同的語(yǔ)義控制(assign、retain、copy)的setter方法實(shí)現(xiàn)的不同,對(duì)于retain和copy來(lái)說(shuō),他們的內(nèi)部實(shí)現(xiàn)都是先把舊值release和把新值retain。另外對(duì)于發(fā)送autorelease消息的對(duì)象會(huì)被加到最近的自動(dòng)釋放池中,當(dāng)自動(dòng)釋放池釋放的時(shí)候,會(huì)給里面的所有對(duì)象發(fā)送一次release消息。iOS5.0之后蘋果推出了ARC、ARC是編譯器的特性,不是OC的語(yǔ)言特性,是編譯器在靜態(tài)編譯的基礎(chǔ)上(command + shift + B),編譯器在合適的地方給我們加了retain、release、autorelease這些代碼,不用我們自己去手動(dòng)寫這些代碼了。ARC中屬性的關(guān)鍵字是strong和weak,其中strong和MRC下的retain作用相同,都是持有一個(gè)對(duì)象,weak和MRC下的assigin類型,是一個(gè)弱引用,不持有一個(gè)對(duì)象,但是weak只能修飾對(duì)象類型,不能修飾基本類型,并且weak會(huì)在指向的對(duì)象被銷毀的時(shí)候指針自動(dòng)置nil。

#import、#include、@class、#import<>和#import”"的區(qū)別

import

  • 是 Objective-C 導(dǎo)入頭文件的關(guān)鍵字,完整地包含某個(gè)文件的內(nèi)容
  • 會(huì)自動(dòng)導(dǎo)入一次,不會(huì)重復(fù)導(dǎo)入
  • 不會(huì)引發(fā)交叉編譯; 因?yàn)樵?Objective-C 中會(huì)存在C/C++和Objective-C 混編的問(wèn)題,如果用 #include 引入頭文件,會(huì)導(dǎo)致交叉編譯。
  • include
  • C/C++ 導(dǎo)入頭文件的關(guān)鍵字,完整地包含某個(gè)文件的內(nèi)容
  • @class 1. 僅僅是聲明一個(gè)類名,并不會(huì)包含類的完整聲明2.能解決循環(huán)包含的問(wèn)題:當(dāng)兩個(gè)類文件有循環(huán)依賴關(guān)系 ( A 引用 B , B 引用 A ) 時(shí),需要用 @class

import<> 和 import”"

  • <> : 引用系統(tǒng)文件,它用于對(duì)系統(tǒng)自帶的頭文件的引用,編譯器會(huì)在系統(tǒng)文件目錄下去查找該文件.
  • "": 用戶自定義的文件用雙引號(hào)引用,編譯器首先會(huì)在用戶目錄下查找,然后到安裝目錄中查

iOS assign,weak,strong,copy ,atomic, nonatomic詳解

assign 與weak區(qū)別

  • assign適用于基本數(shù)據(jù)類型,weak是適用于NSObject對(duì)象,并且是一個(gè)弱引用。assign其實(shí)也可以用來(lái)修飾對(duì)象。那么我們?yōu)槭裁床挥盟揎棇?duì)象呢?因?yàn)楸籥ssign修飾的對(duì)象(一般編譯的時(shí)候會(huì)產(chǎn)生警告:Assigning retained object to unsafe property; object will be released after assignment)在釋放之后,指針的地址還是存在的,也就是說(shuō)指針并沒(méi)有被置為nil,造成野指針。對(duì)象一般分配在堆上的某塊內(nèi)存,如果在后續(xù)的內(nèi)存分配中,剛好分到了這塊地址,程序就會(huì)崩潰掉。
  • 那為什么可以用assign修飾基本數(shù)據(jù)類型?因?yàn)榛A(chǔ)數(shù)據(jù)類型一般分配在棧上,棧的內(nèi)存會(huì)由系統(tǒng)自己自動(dòng)處理,不會(huì)造成野指針。weak修飾的對(duì)象在釋放之后,指針地址會(huì)被置為nil。所以現(xiàn)在一般弱引用就是用weak。
  • weak使用場(chǎng)景:在ARC下,在有可能出現(xiàn)循環(huán)引用的時(shí)候,往往要通過(guò)讓其中一端使用weak來(lái)解決,比如: delegate代理屬性,通常就會(huì)聲明為weak。自身已經(jīng)對(duì)它進(jìn)行一次強(qiáng)引用,沒(méi)有必要再?gòu)?qiáng)引用一次時(shí)也會(huì)使用weak。比如:自定義 IBOutlet控件屬性一般也使用weak,當(dāng)然也可以使用strong。

strong 與copy的區(qū)別

  • strong 與copy都會(huì)使引用計(jì)數(shù)(retain)加1,但strong是兩個(gè)指針指向同一個(gè)內(nèi)存地址,copy會(huì)在內(nèi)存里拷貝一份對(duì)象,兩個(gè)指針指向不同的內(nèi)存地址
  • __block與__weak的區(qū)別
  • __block是用來(lái)修飾一個(gè)變量,這個(gè)變量就可以在block中被修改 __block:使用 __block修飾的變量在block代碼塊中會(huì)被retain(ARC下會(huì)retain,MRC下不會(huì)retain)__weak:使用__weak修飾的變量不會(huì)在block代碼塊中被retain 同時(shí),在ARC下,要避免block出現(xiàn)循環(huán)引用 __weak typedof(self)weakSelf = self;
  • block變量定義時(shí)為什么用copy?block是放在哪里的?
  • block的循環(huán)引用并不是strong導(dǎo)致的…在ARC環(huán)境下,系統(tǒng)底層也會(huì)做一次copy操作使block從棧區(qū)復(fù)制一塊內(nèi)存空間到堆區(qū)…所以strong和copy在對(duì)block的修飾上是沒(méi)有本質(zhì)區(qū)別的,只不過(guò)copy操作效率高而已

nonatomic/atomic

  • atomic的意思就是setter/getter這兩個(gè)函數(shù)的一個(gè)原語(yǔ)操作。如果有多個(gè)線程同時(shí)調(diào)用setter的話,不會(huì)出現(xiàn)某一個(gè)線程執(zhí)行setter全部語(yǔ)句之前,另一個(gè)線程開(kāi)始執(zhí)行setter情況,相當(dāng)于函數(shù)頭尾加了鎖一樣。 nonatomic不保證setter/getter的原語(yǔ)行,所以你可能會(huì)取到不完整的東西。 比如setter函數(shù)里面改變兩個(gè)成員變量,如果你用nonatomic的話,getter可能會(huì)取到只更改了其中一個(gè)變量時(shí)候的狀態(tài)。 atomic是線程安全的,nonatomic是線程不安全的。如果只是單線程操作的話用nonatomic最好,因?yàn)楹笳咝矢咭恍?/li>

OC之面向?qū)ο蟮娜筇卣?/h2>
  • 封裝

封裝是對(duì)象和類概念的主要特性。它是隱藏內(nèi)部實(shí)現(xiàn),穩(wěn)定外部接口,可以看作是“包裝”。封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對(duì)象操作,對(duì)不可信的進(jìn)行信息隱藏。
好處:使用更簡(jiǎn)單變量更安全可以隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié)開(kāi)發(fā)速度加快
OC中一個(gè)類可以繼承另一個(gè)類,被繼承的類成為超類(superclass),繼承的類是子類(childclass),可以直接擁有父類中所有非私有成員(相關(guān)實(shí)例變量)、方法。繼承的實(shí)現(xiàn)在接口中使用符號(hào)“:”。
舉個(gè)例子:@interfaceStudent:NSObject{}不過(guò)大家要注意的是:屬性封裝實(shí)例變量,方法封裝具體實(shí)現(xiàn)代碼,類封裝屬性和方法。子類可繼承父類中的方法,還可重寫父類方法。

  • 多態(tài)

多態(tài)性(polymorphism)是允許你將父對(duì)象設(shè)置成為和一個(gè)或更多的他的子對(duì)象相等的技術(shù),賦值之后,父對(duì)象就可以根據(jù)當(dāng)前賦值給它的子對(duì)象的特性以不同的方式運(yùn)作。簡(jiǎn)單的說(shuō),就是一句話:允許將子類類型的指針賦值給父類類型的指針。不同對(duì)象以自己的方式響應(yīng)相同的消息的能力叫做多態(tài)。意思就是假設(shè)生物類(life)都用有一個(gè)相同的方法-eat;那人類屬于生物,豬也屬于生物,都繼承了life后,實(shí)現(xiàn)各自的eat,但是調(diào)用是我們只需調(diào)用各自的eat方法。也就是不同的對(duì)象以自己的方式響應(yīng)了相同的消息(響應(yīng)了eat這個(gè)選擇器)。
實(shí)現(xiàn)多態(tài),有二種方式,覆蓋,重載。
1)覆蓋:是指子類重新定義父類的虛函數(shù)的做法。
2)重載:是指允許存在多個(gè)同名函數(shù),而這些函數(shù)的參數(shù)表不同(或許參數(shù)個(gè)數(shù)不同,或許參數(shù)類型不同,或許兩者都不同)。

  • 繼承

面向?qū)ο缶幊?OOP)語(yǔ)言的一個(gè)主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能,并在無(wú)需重新編寫原來(lái)的類的情況下對(duì)這些功能進(jìn)行擴(kuò)展。
通過(guò)繼承創(chuàng)建的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”、“父類”或“超類”。繼承的過(guò)程,就是從一般到特殊的過(guò)程。在考慮使用繼承時(shí),有一點(diǎn)需要注意,那就是兩個(gè)類之間的關(guān)系應(yīng)該是“屬于”關(guān)系。例如,Employee是一個(gè)人,Manager也是一個(gè)人,因此這兩個(gè)類都可以繼承Person類。但是Leg類卻不能繼承Person類,因?yàn)橥炔⒉皇且粋€(gè)人。

小結(jié):封裝可隱藏實(shí)現(xiàn)細(xì)節(jié),,使代碼模塊化;繼承可擴(kuò)展已存在的代碼模塊(類);它們最終需要的結(jié)果(代碼重用)。多態(tài)是為了實(shí)現(xiàn)另一個(gè)目的(接口重用)。多態(tài)的作用,就是為了類在繼承和派生的時(shí)候,保證使用“家譜”中任一類的實(shí)例的某一屬性時(shí)的正確調(diào)用。

事件響應(yīng)者鏈的概念(responder chain);

響應(yīng)者鏈表示一系列的響應(yīng)者對(duì)象.事件被交給由第一響應(yīng)者對(duì)象處理,如果第一響應(yīng)者不處理,事件被沿著響應(yīng)者鏈向上傳遞,交給下一響應(yīng)者(next responder).一般來(lái)說(shuō),第一響應(yīng)者是視圖對(duì)象或者其子類對(duì)象,當(dāng)其被觸摸后事件被交由它處理,如果它不處理,事件就會(huì)被傳遞給它的視圖控制器對(duì)象(如果存在),然后就是它的父視圖(superView)對(duì)象(如果存在),以此類推,直到頂層視圖.接下來(lái)會(huì)沿著頂層視圖(top view)到窗口(UIWindow對(duì)象)再到程序(UIApplication對(duì)象).如果整個(gè)過(guò)程都沒(méi)有響應(yīng)這個(gè)事件,該事件就會(huì)被丟棄.一般情況下,在響應(yīng)者中只要由對(duì)象處理事件,事件就停止傳遞.但有時(shí)候可以在視圖扥響應(yīng)方法中根據(jù)一些條件判斷來(lái)決定是否需要繼續(xù)傳遞事件.

請(qǐng)簡(jiǎn)述對(duì)MVC / MVVM 設(shè)計(jì)模式的理解

MVC設(shè)計(jì)模式

Model:負(fù)責(zé)存儲(chǔ)、定義、操作數(shù)據(jù);
View:負(fù)責(zé)呈現(xiàn)畫面以及與用戶進(jìn)行操作交互
Controller:Model和View的協(xié)調(diào)者,Controller把Model中的數(shù)據(jù)拿過(guò)來(lái)給View用。
Controller可以直接與Model和View進(jìn)行通信,而View不能和Controller直接通信。View與Controller通信需要利用代理協(xié)議的方式,當(dāng)有數(shù)據(jù)更新時(shí),Model也要與Controller進(jìn)行通信,這個(gè)時(shí)候就要用Notification和KVO,這個(gè)方式就像廣播一樣,Model發(fā)信號(hào),Controller設(shè)置監(jiān)聽(tīng)接收信號(hào),當(dāng)有數(shù)據(jù)更新時(shí)就發(fā)信號(hào)給Controller,Model和View不能直接進(jìn)行通信,這樣會(huì)違背MVC設(shè)計(jì)模式。

如何理解MVVM設(shè)計(jì)模式

ViewModel層,就是View和Model層的粘合劑,他是一個(gè)放置用戶輸入驗(yàn)證邏輯,視圖顯示邏輯,發(fā)起網(wǎng)絡(luò)請(qǐng)求和其他各種各樣的代碼的極好的地方。說(shuō)白了,就是把原來(lái)ViewController層的業(yè)務(wù)邏輯和頁(yè)面邏輯等剝離出來(lái)放到ViewModel層。
View層,就是ViewController層,他的任務(wù)就是從ViewModel層獲取數(shù)據(jù),然后顯示。

請(qǐng)簡(jiǎn)述HTTP協(xié)議中g(shù)et請(qǐng)求和post請(qǐng)求的區(qū)別,同步請(qǐng)求和異步請(qǐng)求的區(qū)別

get請(qǐng)求

參數(shù)在地址后拼接,進(jìn)行請(qǐng)求數(shù)據(jù),不安全(因?yàn)樗袇?shù)都拼接在地址后面),不適合傳輸大量數(shù)據(jù)(長(zhǎng)度限制,為256個(gè)字節(jié))。get提交、請(qǐng)求的數(shù)據(jù)會(huì)附在URL之后,即把數(shù)據(jù)放置在HTTP協(xié)議頭中。以分割URL和傳輸數(shù)據(jù),多個(gè)參數(shù)用&連接。如果數(shù)據(jù)是英文字母或數(shù)字,原樣發(fā)送,如果是空格,轉(zhuǎn)換為+,如果是中文/其他字符,則直接把字符串用BASE64加密。

post請(qǐng)求

參數(shù)在請(qǐng)求數(shù)據(jù)區(qū)放著,相對(duì)get請(qǐng)求更安全,并且數(shù)據(jù)大小沒(méi)有限制(1G)。把提交的數(shù)據(jù)放置在HTTP包的包體中,轉(zhuǎn)換成NSData進(jìn)行傳輸。
get提交的數(shù)據(jù)會(huì)在地址欄顯示出來(lái),而post提交,地址欄不會(huì)改變。

傳輸數(shù)據(jù)的大小:

get請(qǐng)求時(shí),傳輸數(shù)據(jù)就會(huì)受到URL長(zhǎng)度限制,POST由于不是通過(guò)URL傳輸,理論上不受限。

  • 安全性:post的安全性要比get的安全性高;通過(guò)get提交數(shù)據(jù),用戶名和密碼將明文出現(xiàn)在URL上,比如登錄界面有可能被瀏覽器緩存。

同步請(qǐng)求/異步請(qǐng)求

  • 1.同步請(qǐng)求可以從網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù),一旦發(fā)送同步請(qǐng)求,程序?qū)⑼V古c用戶交互,直到服務(wù)器返回?cái)?shù)據(jù)完成,才可以進(jìn)行下一步操作;
  • 2.異步請(qǐng)求不會(huì)阻塞主線程,而會(huì)建立一個(gè)新的線程來(lái)操作,用戶發(fā)出異步請(qǐng)求后,依然可以對(duì)UI進(jìn)行操作,程序可以繼續(xù)運(yùn)行;

多線程編程N(yùn)SThread/NSoperationQueue/NSObject/GCD 區(qū)別

單線程
只有一個(gè)線程的程序即為單線程.該線程被稱為主線程.在程序一運(yùn)行的時(shí)候就存在了.

問(wèn)題:
因?yàn)橹挥幸粋€(gè)線程,所有代碼的執(zhí)行只能從上往下順序執(zhí)行.如果有耗時(shí)的操作(網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)解析等),會(huì)造成界面假死,影響用戶體驗(yàn)/
解決:
將一些耗時(shí)的操作放在子線程中去執(zhí)行,主線程只用來(lái)UI的展示和刷新;

多線程
除了主線程外,還存在其他線程的程序即為多線程程序

注意事項(xiàng):
所有跟UI相關(guān)的操作都必須放在主線程中;

問(wèn)題:
既然多線程程序有這么多的好處,該如何開(kāi)辟子線程?

解決方案:
iOS中可以使用NSThread/NSoperationQueue/NSObject/GCD來(lái)開(kāi)辟子線程.

問(wèn)題:
無(wú)論開(kāi)辟了多少子線程,最終的目的都要回到主線程刷新UI,那么該如何從子線程回主線程?

解決方案:
1.使用performSelectorOnMainThread方法;
2.使用GCD中dispatch_async(dispath_get_mainQueue(),);

問(wèn)題:
既然多線程可以解決界面假死現(xiàn)象,那么是不是開(kāi)辟的線程越多越好?
答:
不是。開(kāi)辟子線程需要消耗內(nèi)存,而且不管開(kāi)辟了多少子線程。最終都是要回到主線程的。所以開(kāi)辟的子線程越多對(duì)于CPU性能的消耗越大。所以根據(jù)需要開(kāi)辟適當(dāng)?shù)木€程。

問(wèn)題:
如何讓實(shí)現(xiàn)線程同步(里面任務(wù)一個(gè)個(gè)執(zhí)行)
答:
1.GCD里面使用串行隊(duì)列.
2.NSOperationQueue里面當(dāng)把最大并發(fā)數(shù)發(fā)送設(shè)置1的時(shí)候,也可以實(shí)現(xiàn)線程同步;

NSObject

@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
  • 使用NSObject開(kāi)辟子線程方式:performSelectorInBackground;該方法調(diào)用一次,開(kāi)辟一個(gè)子線程;
  • 使用performSelectorOnMainThread方法回到主線程

NSThread:
當(dāng)需要進(jìn)行一些耗時(shí)操作時(shí)會(huì)把耗時(shí)的操作放到線程中。線程同步:多個(gè)線程同時(shí)訪問(wèn)一個(gè)數(shù)據(jù)會(huì)出問(wèn)題,NSlock、線程同步塊、@synchronized(self){}。

NSThread開(kāi)辟子線程的方式有兩種

  /**
   *   第一種方式
   * - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)
   *  @param target   目標(biāo)
   *  @param selector 方法(如果方法有參數(shù),那么參數(shù)即為object傳入的參數(shù))
   *  @param object 參數(shù)
   *
   *  @return  NSThread對(duì)象
   *  注意事項(xiàng):需要手動(dòng)調(diào)用start方法
   */
 /**
     *  第二種方法(NSThread類方法)
     *
   * + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
     *  @param SEL 方法(如果方法有參數(shù),那么參數(shù)即為object傳入的參數(shù))
     *
     *  自動(dòng)開(kāi)啟線程,需手動(dòng)取消
     */

使用事項(xiàng):
使用NSThread開(kāi)辟的子線程中需要添加@autoreleasepool來(lái)釋放在對(duì)應(yīng)子線程中開(kāi)辟的內(nèi)存,其實(shí)在主線程中也設(shè)有@autoreleasepool.

NSOperationQueue

NSOperationQueue操作隊(duì)列(不需考慮線程同步問(wèn)題)。編程的重點(diǎn)都放在main里面,使用NSInvocationOperation、NSBlockOperation自定義Operation。創(chuàng)建一個(gè)操作綁定相應(yīng)的方法,當(dāng)把操作添加到操作隊(duì)列中時(shí),操作綁定的方法就會(huì)自動(dòng)執(zhí)行了,當(dāng)把操作添加到操作隊(duì)列中時(shí),默認(rèn)會(huì)調(diào)用main方法。

NSOPeration是系統(tǒng)提供的一個(gè)用來(lái)封裝任務(wù)的抽象類.

  • 注意事項(xiàng):
    1.NSOperation沒(méi)有線程之分.把它放在主線程,它就在主線程中執(zhí)行.把它放在子線程中,它就在子線程中執(zhí)行.
    2.NSOperation是抽象類,在真實(shí)的開(kāi)發(fā)中不會(huì)直接使用NSOperation,而是使用其子類:NSInvocationOperation(target-action)和NSBlockOperation(block);

  • NSOperationQueue: 操作隊(duì)列,存放的是一個(gè)個(gè)操作(NSOperation)

  • Tips

    • 1.NSOperationQueue可以實(shí)現(xiàn)多線程,即它會(huì)根據(jù)放在隊(duì)列的任務(wù)來(lái)自行開(kāi)辟子線程,由系統(tǒng)來(lái)決定,跟程序員沒(méi)有半毛錢關(guān)系.(坑:我現(xiàn)在往操作隊(duì)列中放兩個(gè)任務(wù), 那么需要幾線程?)
    • 2.當(dāng)把操作放在操作隊(duì)列中之后,無(wú)需手動(dòng)調(diào)用start.
    • 3.最大并發(fā)數(shù)決定了當(dāng)前一共有幾個(gè)任務(wù)正在執(zhí)行.如果設(shè)置為1;則意味著當(dāng)前只有一個(gè)任務(wù)正在執(zhí)行.可以實(shí)現(xiàn)線程同步(串行執(zhí)行)和線程個(gè)數(shù)沒(méi)有關(guān)系.因?yàn)榫€程個(gè)數(shù)由系統(tǒng)來(lái)決定的.

GCD

GCD(`Grand Central Dispatch)宏大的中央調(diào)度,串行隊(duì)列、并發(fā)隊(duì)列、主線程隊(duì)列;

  • 同步和異步:同步指第一個(gè)任務(wù)不執(zhí)行完,不會(huì)開(kāi)始第二個(gè),異步是不管第一個(gè)有沒(méi)有執(zhí)行完,都開(kāi)始第二個(gè)。

  • 串行和并行:串行是多個(gè)任務(wù)按一定順序執(zhí)行,并行是多個(gè)任務(wù)同時(shí)執(zhí)行;
    代碼是在分線程執(zhí)行,在主線程嘟列中刷新UI。

  • GCD:GCD核心是將任務(wù)放在分發(fā)隊(duì)列中去執(zhí)行.
    分發(fā)隊(duì)列分為兩種:

  • 串行隊(duì)列:SerialQueue.如果將任務(wù)放在一個(gè)串行隊(duì)列中.那么任務(wù)會(huì)順序執(zhí)行.遵循FIFO,可以實(shí)現(xiàn)線程同步.但是當(dāng)有多個(gè)serialQueue時(shí),串行隊(duì)列和串行隊(duì)列中的任務(wù)可以實(shí)現(xiàn)并發(fā)執(zhí)行.

串行隊(duì)列兩種:

  • 系統(tǒng)自帶的 : 主隊(duì)列;當(dāng)把任務(wù)放在主隊(duì)列中時(shí),任務(wù)會(huì)按照順序在主隊(duì)列中執(zhí)行.
    dispatch_queue_t queue = dispatch_get_main_queue();
  • 自己創(chuàng)建的 : 自己創(chuàng)建的隊(duì)列中的任務(wù)會(huì)在其他線程中順序執(zhí)行.
    dispatch_queue_t queue = dispatch_queue_create(“com.xxw.serialQueue", DISPATCH_QUEUE_SERIAL);

并行隊(duì)列分為兩種:

  • 1.系統(tǒng)自帶的: 使用系統(tǒng)提供好的globalQueue會(huì)開(kāi)辟子線程來(lái)執(zhí)行任務(wù),任務(wù)的執(zhí)行是并發(fā)執(zhí)行的.
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  • 2.自己創(chuàng)建的: 一般情況下,不回你自己創(chuàng)建并行隊(duì)列,因?yàn)橄到y(tǒng)提供好的globalQueue(在子線程)可以滿足需求.
    dispatch_queue_t queue = dispatch_queue_create("com.fy.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

  • GCD其他用法

  • 1.使用dispatch_after 延遲特定的時(shí)間去做某事
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });

  • 2.dispatch_sync函數(shù)會(huì)阻塞當(dāng)前線程,去對(duì)應(yīng)的隊(duì)列中去執(zhí)行block中的任務(wù),當(dāng)任務(wù),執(zhí)行完之后才會(huì)回到原來(lái)的線程中
    dispatch_sync(dispatch_get_main_queue(), ^{ });

  • 3..dispatch_apply重復(fù)執(zhí)行
    dispatch_apply(arr.count, dispatch_queue_create("com.fy.apply", DISPATCH_QUEUE_SERIAL), ^(size_t i) {});

  • 4.barrier 函數(shù)在使用的時(shí)候隊(duì)列必須是自己創(chuàng)建的的并行隊(duì)列,否則barrier順序起不到作用;
    dispatch_queue_t queue = dispatch_queue_create("com.fy.barrier", DISPATCH_QUEUE_CONCURRENT); dispatch_barrier_async(queue, ^{ dispatch_async(queue, ^{ }); dispatch_async(queue, ^{ }); });

  • 5.使用group_notify函數(shù)/ 只有當(dāng)小組內(nèi)所有任務(wù)完成之后,才會(huì)執(zhí)行g(shù)roup_notify里面的內(nèi)容(PS:小組內(nèi)最起碼要有一組任務(wù));
    dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_main_queue(), ^{ }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ }); dispatch_group_async(group, dispatch_get_main_queue(), ^{ });

  • 6.dispatch_once_t
    static dispatch_once_t oneToken; dispatch_once(&oneToken, ^{ NSLog(@"你就是我的唯一"); });

總結(jié):
在真正開(kāi)發(fā)的過(guò)程中,一般會(huì)使用自己創(chuàng)建的串行隊(duì)列或者是系統(tǒng)提供好的并行隊(duì)列;

NSOPerationQueue 與 GCD 的區(qū)別與選用

GCD 技術(shù)是一個(gè)輕量的,底層實(shí)現(xiàn)隱藏的神奇技術(shù),我們能夠通過(guò)GCD和block輕松實(shí)現(xiàn)多線程編程,有時(shí)候,GCD相比其他系統(tǒng)提供的多線程方法更加有效,當(dāng)然,有時(shí)候GCD不是最佳選擇,另一個(gè)多線程編程的技術(shù) NSOprationQueue 讓我們能夠?qū)⒑笈_(tái)線程以隊(duì)列方式依序執(zhí)行,并提供更多操作的入口,這和 GCD 的實(shí)現(xiàn)有些類似。
這種類似不是一個(gè)巧合,在早期,MacOX 與 iOS 的程序都普遍采用Operation Queue來(lái)進(jìn)行編寫后臺(tái)線程代碼,而之后出現(xiàn)的GCD技術(shù)大體是依照前者的原則來(lái)實(shí)現(xiàn)的,而隨著GCD的普及,在iOS 4 與 MacOS X 10.6以后,Operation Queue的底層實(shí)現(xiàn)都是用GCD來(lái)實(shí)現(xiàn)的。

那這兩者直接有什么區(qū)別呢?

  1. GCD是底層的C語(yǔ)言構(gòu)成的API,而NSOperationQueue及相關(guān)對(duì)象是Objc的對(duì)象。在GCD中,在隊(duì)列中執(zhí)行的是由block構(gòu)成的任務(wù),這是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu);而Operation作為一個(gè)對(duì)象,為我們提供了更多的選擇;
  2. 在NSOperationQueue中,我們可以隨時(shí)取消已經(jīng)設(shè)定要準(zhǔn)備執(zhí)行的任務(wù)(當(dāng)然,已經(jīng)開(kāi)始的任務(wù)就無(wú)法阻止了),而GCD沒(méi)法停止已經(jīng)加入queue的block(其實(shí)是有的,但需要許多復(fù)雜的代碼);
  3. NSOperation能夠方便地設(shè)置依賴關(guān)系,我們可以讓一個(gè)Operation依賴于另一個(gè)Operation,這樣的話盡管兩個(gè)Operation處于同一個(gè)并行隊(duì)列中,但前者會(huì)直到后者執(zhí)行完畢后再執(zhí)行;
  4. 我們能將KVO應(yīng)用在NSOperation中,可以監(jiān)聽(tīng)一個(gè)Operation是否完成或取消,這樣子能比GCD更加有效地掌控我們執(zhí)行的后臺(tái)任務(wù);
  5. 在NSOperation中,我們能夠設(shè)置NSOperation的priority優(yōu)先級(jí),能夠使同一個(gè)并行隊(duì)列中的任務(wù)區(qū)分先后地執(zhí)行,而在GCD中,我們只能區(qū)分不同任務(wù)隊(duì)列的優(yōu)先級(jí),如果要區(qū)分block任務(wù)的優(yōu)先級(jí),也需要大量的復(fù)雜代碼;
  6. 我們能夠?qū)SOperation進(jìn)行繼承,在這之上添加成員變量與成員方法,提高整個(gè)代碼的復(fù)用度,這比簡(jiǎn)單地將block任務(wù)排入執(zhí)行隊(duì)列更有自由度,能夠在其之上添加更多自定制的功能。

總的來(lái)說(shuō),Operation queue 提供了更多你在編寫多線程程序時(shí)需要的功能,并隱藏了許多線程調(diào)度,線程取消與線程優(yōu)先級(jí)的復(fù)雜代碼,為我們提供簡(jiǎn)單的API入口。從編程原則來(lái)說(shuō),一般我們需要盡可能的使用高等級(jí)、封裝完美的API,在必須時(shí)才使用底層API。但是我認(rèn)為當(dāng)我們的需求能夠以更簡(jiǎn)單的底層代碼完成的時(shí)候,簡(jiǎn)潔的GCD或許是個(gè)更好的選擇,而Operation queue 為我們提供能更多的選擇。

NSOperationQueue和GCD的區(qū)別,以及在什么場(chǎng)合下使用

  1. GCD是純C語(yǔ)言的API 。NSOperationQueue是基于GCD的OC的封裝。
  2. GCD只支持FIFO隊(duì)列,NSOperationQueue可以方便設(shè)置執(zhí)行順序,設(shè)置最大的并發(fā)數(shù)量。
  3. NSOperationQueue可是方便的設(shè)置operation之間的依賴關(guān)系,GCD則需要很多代碼。
  4. NSOperationQueue支持KVO,可以檢測(cè)operation是否正在執(zhí)行(isExecuted),是否結(jié)束(isFinished),是否取消(isCanceled)
  5. GCD的執(zhí)行速度比NSOperationQueue快。

使用場(chǎng)合

  • 任務(wù)之間不太相互依賴:GCD
  • 任務(wù)之間有依賴或要監(jiān)聽(tīng)任務(wù)的執(zhí)行情況:NSOperationQueue

iOS中的沙盒機(jī)制

iOS應(yīng)用程序只能對(duì)自己創(chuàng)建的文件系統(tǒng)讀取文件,這個(gè)獨(dú)立、封閉、安全的空間,叫做沙盒。每個(gè)ios應(yīng)用都有自己的應(yīng)用沙盒,應(yīng)用沙盒就是文件系統(tǒng)目錄,與其他應(yīng)用的文件系統(tǒng)隔離。它一般存放著程序包文件(可執(zhí)行文件)、圖片、音頻、視頻、plist文件、sqlite數(shù)據(jù)庫(kù)以及其他文件。每個(gè)應(yīng)用程序都有自己的獨(dú)立的存儲(chǔ)空間(沙盒)一般來(lái)說(shuō)應(yīng)用程序之間是不可以互相訪問(wèn)的,在ios8中已經(jīng)開(kāi)放訪問(wèn),模擬器沙盒的位置路徑:/User/userName/Library/Application Support/iPhone Simulator;當(dāng)我們創(chuàng)建應(yīng)用程序時(shí),在每個(gè)沙盒中含有三個(gè)文件,分別是Document、Library(下面有Caches和Preferences目錄)和temp。
應(yīng)用程序包:包含所有的資源文件和可執(zhí)行文件。

對(duì)Document、Library(下面有Caches和Preferences目錄)和temp做簡(jiǎn)單介紹

  1. Document:一般需要持久的數(shù)據(jù)都放在此目錄中,可以在當(dāng)中添加子文件夾,iTunes備份和恢復(fù)的時(shí)候,會(huì)包括此目錄。
  2. Library:設(shè)置程序的默認(rèn)設(shè)置和其他狀態(tài)信息,iTunes會(huì)自動(dòng)備份該目錄,例如雜志、新聞、地圖應(yīng)用使用的數(shù)據(jù)庫(kù)緩存文件和可下載內(nèi)容應(yīng)該保存到這個(gè)文件夾。一般可以重新下載或者重新生成的數(shù)據(jù)應(yīng)該保存在 <Application_Home>/Library/Caches 目錄下面。
    Libaray/Caches:存放緩存文件,iTunes不會(huì)備份此目錄,此目錄下文件不會(huì)在應(yīng)用退出刪除。一般存放體積比較大,不是特別重要的資源。
    Libaray/PreferencePanes:保存應(yīng)用的所有偏好設(shè)置,ios的Settings(設(shè)置)應(yīng)用會(huì)在該目錄中查找應(yīng)用的設(shè)置信息,iTunes會(huì)自動(dòng)備份該目錄。
  3. temp:創(chuàng)建臨時(shí)文件的目錄,當(dāng)iOS設(shè)備重啟時(shí),文件會(huì)被自動(dòng)清除,只是臨時(shí)使用的

防止iCloud backup

  • 正確放置文件 見(jiàn)上面一條
  • 使用 NSURLIsExcludedFromBackupKey or kCFURLIsExcludedFromBackupKey 防止文件 或者整個(gè)文件夾 iCloud 備份
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString

{

NSURL* URL= [NSURL fileURLWithPath: filePathString];
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
}
return success;
}

initialize 和 load

在Objective-C中,runtime會(huì)自動(dòng)調(diào)用每個(gè)類的兩個(gè)方法。+load會(huì)在類初始加載時(shí)調(diào)用,+initialize會(huì)在第一次調(diào)用類的類方法或?qū)嵗椒ㄖ氨徽{(diào)用。這兩個(gè)方法是可選的,且只有在實(shí)現(xiàn)了它們時(shí)才會(huì)被調(diào)用。
共同點(diǎn):兩個(gè)方法都只會(huì)被調(diào)用一次。

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

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

  • NSThread 第一種:通過(guò)NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 947評(píng)論 0 3
  • 學(xué)習(xí)多線程,轉(zhuǎn)載兩篇大神的帖子,留著以后回顧!第一篇:關(guān)于iOS多線程,你看我就夠了 第二篇:GCD使用經(jīng)驗(yàn)與技巧...
    John_LS閱讀 723評(píng)論 0 3
  • 在這篇文章中,我將為你整理一下 iOS 開(kāi)發(fā)中幾種多線程方案,以及其使用方法和注意事項(xiàng)。當(dāng)然也會(huì)給出幾種多線程的案...
    張戰(zhàn)威ican閱讀 684評(píng)論 0 0
  • 今天微博和朋友圈都被無(wú)良的百度刷屏了,且不說(shuō)今天百度莆田系部隊(duì)醫(yī)院這件事情。因?yàn)檫@種無(wú)良的企業(yè),死去的何止一個(gè)魏澤...
    許多的yolyol閱讀 738評(píng)論 2 1
  • 最近閑著沒(méi)事做,想去看部電影,打開(kāi)美團(tuán)看到百鳥朝鳳評(píng)分有9.4,還是特惠19.9.大致看了下評(píng)價(jià),都是說(shuō)很感動(dòng),...
    未知未來(lái)閱讀 415評(píng)論 0 2

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