iOS面試(三)
1.MVC具有什么樣的優(yōu)勢(shì),各個(gè)模塊之間怎么通信惠爽,比如Button后怎么通知Model
MVC是一種設(shè)計(jì)思想,是一種架構(gòu)模式瑟押,是一種把應(yīng)用所有類(lèi)組織起來(lái)的策略彪薛。他們把程序分成3塊寂呛。
M: 存儲(chǔ)數(shù)據(jù)缤灵,處理程序的邏輯部分伦籍。
C: 控制你的model如何呈現(xiàn)在屏幕上蓝晒。他需要數(shù)據(jù)的時(shí)候會(huì)去Model獲取數(shù)據(jù)。需要視圖顯示的時(shí)候會(huì)通知視圖顯示鸽斟。它是model和view之間的橋梁拔创。它使model和view解耦利诺。
V:視圖的展示富蓄。根據(jù)model來(lái)創(chuàng)建視圖。
Controller to Model
可以單向通信慢逾×⒈叮控制器需要知道m(xù)odel中的一切。還要同model完全的通信的能力侣滩。
Controller to View
可以直接進(jìn)行單向通信口注,Controller通過(guò)View來(lái)布局界面
Model to View
永遠(yuǎn)不要直接通信。Model是獨(dú)立于UI的存在的君珠。View 通過(guò)Controller獲取Model數(shù)據(jù)寝志。
View to Controller
View不能對(duì)Controller知道的太多。隱藏他們使用間接通信的方式
- targer action
- delegate
- dataSource
Model to Controller
Model獨(dú)立于UI存在的策添。Model無(wú)法直接訪(fǎng)問(wèn)Controller這樣就綁死了材部。通過(guò)Notification&&KVO
優(yōu)點(diǎn):
- 低耦合性
- 開(kāi)發(fā)分工
- 有利于組件重用
- 可維護(hù)性
2. UITableView 的相關(guān)優(yōu)化
從幾個(gè)角度去分析
-
基礎(chǔ)優(yōu)化(高度緩存,cell重用)
- 正確使用TableViewCell的緩存機(jī)制
- 提前計(jì)算機(jī)cell的高度唯竹。把cell的高度緩存起來(lái)乐导。因?yàn)閔eight forRow 這個(gè)方法會(huì)頻繁的調(diào)用所以不適合存在大量的計(jì)算。
- 下載圖片避免阻塞主線(xiàn)程
- 按需求加載浸颓。設(shè)置一個(gè)需要加載的Arr物臂。超過(guò)加載范圍的cell清除顯示的內(nèi)容。會(huì)使得滾動(dòng)的范圍內(nèi)出現(xiàn)大量的留白产上。但是會(huì)僅繪制目標(biāo)的cell
- 減少subViews的數(shù)量
- 盡量重用開(kāi)銷(xiāo)比較大的對(duì)象
- 減少?gòu)?fù)雜的計(jì)算
- 不要?jiǎng)討B(tài)add或者remove子控件
-
學(xué)會(huì)使用分析工具
instruments中的Core Animation instrument
instruments中的OpenGL ES Drive instrument
Xcode的debug的一些列的調(diào)試工具
-
異步繪制
- 開(kāi)啟一條異步線(xiàn)程繪制cell
- 繪制Cell使用的是CALayer而不是UIView
- UIView的繪制是建立在Core Graphic上棵磷,使用的是CPU。而CALayer的繪制是建立在Core Animation上晋涣,CPU泽本,GPU均可。UIView的繪制是從下到上一層層的繪制姻僧。然后渲染规丽。CALayer處理的是Texture,利用GPU的Texture Cache和獨(dú)立的浮點(diǎn)數(shù)計(jì)算單元加速文理的處理撇贺。
KVO,Notification,delegate各自的優(yōu)缺點(diǎn)
- delegate 委托
- 通知中心 NotificationCenter
- KVO鍵值觀(guān)察
delegate的優(yōu)勢(shì):
- 嚴(yán)格的語(yǔ)法赌莺,協(xié)議清晰
- 不會(huì)遺忘。
- 可以有返回值松嘶,delegate可以反饋信息給Controller
- 出現(xiàn)問(wèn)題可以很好的定位
缺點(diǎn):
代碼較多
NotificationCenter優(yōu)點(diǎn):
- 1對(duì)多
- 使用簡(jiǎn)單
缺點(diǎn):
- 無(wú)法對(duì)發(fā)出通知的路徑進(jìn)行追蹤
- 當(dāng)監(jiān)聽(tīng)者銷(xiāo)毀的時(shí)候需要取消監(jiān)聽(tīng)
- 發(fā)送者無(wú)法確定監(jiān)聽(tīng)者收到了消息
KVO
- 使用簡(jiǎn)單可以實(shí)現(xiàn)2個(gè)對(duì)象同步
- 因?yàn)槭褂米址梢郧短子^(guān)察
確定 - 字符串不會(huì)報(bào)警告
- 如果一個(gè)controller觀(guān)察多個(gè)屬性 需要寫(xiě)很多的if else{}
如何手動(dòng)通知 KVO
控制器中重寫(xiě)某個(gè)屬性的set方法艘狭。在控制器中監(jiān)聽(tīng)。
Objective-C 中的copy方法
對(duì)象的復(fù)制就是復(fù)制一個(gè)對(duì)象作為副本。它會(huì)開(kāi)辟一塊的內(nèi)存來(lái)存儲(chǔ)副本對(duì)象巢音。就像文件復(fù)制一樣遵倦。源對(duì)象和副本對(duì)象是兩塊內(nèi)存。對(duì)象如果想要實(shí)現(xiàn)復(fù)制功能官撼。必須遵循NSCopying協(xié)議或者NSMutableCopying協(xié)議梧躺。
copy:產(chǎn)生的對(duì)象是不可變的
mutableCopy產(chǎn)生的對(duì)象是可以變的。
深拷貝和淺拷貝:
淺拷貝指的是復(fù)制對(duì)象本身傲绣,對(duì)象的屬性掠哥,包含的對(duì)象不做復(fù)制
深拷貝既指復(fù)制對(duì)象本身,對(duì)象的熟悉也會(huì)復(fù)制一份
Foundation中支持復(fù)制的類(lèi)秃诵,默認(rèn)是淺復(fù)制
深淺拷貝和retain之間的關(guān)系
- 對(duì)一個(gè)不可變對(duì)象指向copy操作续搀,相當(dāng)于retain
- 當(dāng)我們使用mutableCopy得到的都是一個(gè)可變對(duì)象
- copy一個(gè)可變對(duì)象得到的對(duì)象不可變。
runtime 中菠净,SEL 和 IMP 的區(qū)別
方法名 SEL -- 方法的名稱(chēng)
一個(gè)IMP 指向該方法的具體實(shí)現(xiàn)的函數(shù)指針禁舷,說(shuō)白了IMP就是方法的實(shí)現(xiàn)。
autoreleasePool的使用場(chǎng)景和原理
autoreleasePool 是OC的一個(gè)類(lèi)毅往,他不是天生就有的牵咙,而是我們手動(dòng)創(chuàng)建的。當(dāng)我們創(chuàng)建iPhone工程的時(shí)候煞抬,系統(tǒng)會(huì)為我們創(chuàng)建一個(gè)AutoreleasePool.這個(gè)pool寫(xiě)在main函數(shù)中霜大。它的內(nèi)部有一個(gè)可變數(shù)組 用來(lái)存儲(chǔ)被標(biāo)記autorelease的對(duì)象。
內(nèi)存管理一直是重中之重革答,盡管現(xiàn)在已經(jīng)是ARC時(shí)代战坤,但是理解ARC依然非常重要,只有了解了Autorelease的原理残拐,我們才能算是理解了Objective-C的內(nèi)存管理機(jī)制途茫。
autoreleased 對(duì)象什么時(shí)候釋放
autorelease本質(zhì)就是延遲調(diào)用release,那autoreleased的對(duì)象到底什么時(shí)候釋放
我們都注意到了AutoreleasePool release本質(zhì)是AutorelesePage::pop溪食。那么本質(zhì)執(zhí)行了什么囊卜。
- 每個(gè)線(xiàn)程的autoreleasePool本質(zhì)就是一個(gè)指針的堆棧。
- 每一個(gè)指針代表一個(gè)需要release對(duì)象或者POOL_SENTINEL(哨兵對(duì)象错沃,代表AutoreleasePool的邊界)
- 一個(gè)pool token 就是這個(gè)pool對(duì)于的POOL_SENTINEL對(duì)應(yīng)的內(nèi)存地址栅组。當(dāng)這個(gè)pool被pop之后,所有內(nèi)存地址在 pool token 之后的對(duì)象都會(huì)被 release
- 這個(gè)堆棧 被劃分成一個(gè)以page為節(jié)點(diǎn)的雙向鏈表枢析。pages會(huì)動(dòng)態(tài)的增加和刪除
- Thread-local storage(線(xiàn)程局部存儲(chǔ))指向hotPage,即新增加的autorelease所在的page.
AutoreleasePage內(nèi)部結(jié)構(gòu)
- magic AutoreleasePoolPage 的結(jié)構(gòu)是否完整
- next指向新增的autorelease對(duì)象的下一個(gè)地址玉掸。初始化的時(shí)候指向begin
- thread 當(dāng)前線(xiàn)程
- parent 指向父結(jié)點(diǎn),第一個(gè)結(jié)點(diǎn)的 parent 值為 nil
- child指向子節(jié)點(diǎn)醒叁,最后一個(gè)節(jié)點(diǎn)的child值為nil
next = begin() 代表autoreleasePoolPage為空司浪。當(dāng) next == end() 時(shí)泊业,表示 AutoreleasePoolPage 已滿(mǎn)。
@autoreleasepool{}
轉(zhuǎn)換為C++代碼
extern "C" __declspec(dllimport)
void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport)
void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool()
{atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
本質(zhì)就是結(jié)構(gòu)體構(gòu)造的時(shí)候調(diào)用構(gòu)造方法執(zhí)行push操作啊易。析構(gòu)的時(shí)候執(zhí)行pop函數(shù)
因此autoreleasepool運(yùn)行的過(guò)程可以理解為objc_autoreleasePoolPush()
,[obj autorelease]
,objc_autoreleasePoolPop
AutoreleasePoolPage 的 push 函數(shù)的作用和執(zhí)行過(guò)程吁伺。一個(gè)Push操作其實(shí)就是創(chuàng)建一個(gè)新的autoreleasepool。對(duì)應(yīng)的就是往AutoreleasePage的next插入一個(gè)哨兵對(duì)象租谈。并且返回這個(gè)哨兵對(duì)象的地址做為pop時(shí)候的返回值來(lái)使用篮奄。
push函數(shù)通過(guò)調(diào)用autoreleaseFast 函數(shù)來(lái)執(zhí)行具體的插入操作。
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
分為3種情況垦垂。
- 存在&&不滿(mǎn) 直接插入當(dāng)前AutorelesePage的next位置
- 滿(mǎn)了 創(chuàng)建一個(gè)新的autoreleasePage
- 不存在 新創(chuàng)建一個(gè)
每調(diào)用一次 push 操作就會(huì)創(chuàng)建一個(gè)新的 autoreleasepool 宦搬,即往 AutoreleasePoolPage 中插入一個(gè) POOL_SENTINEL 牙瓢,并且返回插入的 POOL_SENTINEL 的內(nèi)存地址
Autorelease操作
與push操作類(lèi)型劫拗。Push操作是插入一個(gè)哨兵對(duì)象并且返回哨兵對(duì)象的地址供給pop對(duì)象使用。而autorelease插入的則是一個(gè)具體的autorelease對(duì)象矾克。
pop操作
同理页慷,前面提到的objc_autoreleasePoolPop(void*)也是調(diào)用的AutoreleasePoolPage的pop函數(shù)。Pop函數(shù)的入?yún)⒕褪荘ush對(duì)象的返回值胁附。也就是POOL_SENTINEL的內(nèi)存地址酒繁,即pool token。這個(gè)地址之后的所有的對(duì)象進(jìn)行release操作控妻。知道pool token所在的page的next指向pool token為止州袒。
可能會(huì)用到autoreleasePool對(duì)象的地方
- 編寫(xiě)的程序不是基于UI框架
- 創(chuàng)建了一個(gè)輔助線(xiàn)程
- 編寫(xiě)的循環(huán)中創(chuàng)建了大量的臨時(shí)變量
block 為什么會(huì)有循環(huán)引用
Block塊會(huì)對(duì)其中的所有成員進(jìn)行強(qiáng)引用,如果里面的成員也對(duì)它進(jìn)行強(qiáng)引用的話(huà)弓候,會(huì)形成一個(gè)閉合的環(huán)郎哭。形成循環(huán)引用
NSOperation 和 GCD 的區(qū)別
GCD是基于C的底層Api,NSOpertaion屬于Objective-C類(lèi)菇存。iOS首先引入的NSOperation夸研,iOS4之后引入的GCD和NSOpertaionQueue。相對(duì)GCD依鸥。NSOperation優(yōu)勢(shì)
- NSOperation擁有多個(gè)函數(shù)可以調(diào)用
- NSOperationQueue可以設(shè)置多個(gè)Operation的依賴(lài)關(guān)系
- 通過(guò)KVO可以監(jiān)聽(tīng)NSOperation的isExecuted(是否執(zhí)行)亥至,isCancel(是否取消),isFinish(是否完成)
- NSOperationQueue可以方便的管理并發(fā)贱迟。Operation之間的優(yōu)先級(jí)姐扮。
GCD主要與block結(jié)合使用。代碼簡(jiǎn)潔高效衣吠。
GCD也可以實(shí)現(xiàn)復(fù)雜的多線(xiàn)程應(yīng)用茶敏,主要是建立個(gè)個(gè)線(xiàn)程時(shí)間的依賴(lài)關(guān)系這類(lèi)的情況,但是需要自己實(shí)現(xiàn)相比NSOperation要復(fù)雜蒸播。
具體使用哪個(gè)睡榆,依需求而定萍肆。從個(gè)人使用的感覺(jué)來(lái)看,比較合適的用法是:除了依賴(lài)關(guān)系盡量使用GCD胀屿,因?yàn)樘O(píng)果專(zhuān)門(mén)為GCD做了性能上面的優(yōu)化塘揣。
GCD技術(shù)是一個(gè)輕量級(jí)的,底層實(shí)現(xiàn)隱藏的神奇技術(shù)宿崭。通過(guò)gcd和block輕松實(shí)現(xiàn)多線(xiàn)程編程亲铡。有時(shí)間gcd比其他的多線(xiàn)程編程更有效當(dāng)然,有時(shí)候GCD不是最佳選擇葡兑,另一個(gè)多線(xiàn)程編程的技術(shù) NSOprationQueue 讓我們能夠?qū)⒑笈_(tái)線(xiàn)程以隊(duì)列的形式依序執(zhí)行奖蔓,并提供了更多操作的入口。這和GCD的實(shí)現(xiàn)有些類(lèi)似讹堤。
兩者的區(qū)別
- GCD是C語(yǔ)言構(gòu)筑的APi.而NSOpertaionQueue是NSObject對(duì)象吆鹤。GCD由Block構(gòu)建成任務(wù),這是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu)洲守。而Opertaion作為對(duì)象為我們提供了更多的選擇疑务,
- 在NSOperationQueue中,我們可以隨時(shí)取消已經(jīng)設(shè)定要準(zhǔn)備執(zhí)行的任務(wù)
- NSOperation能夠方便地設(shè)置依賴(lài)關(guān)系梗醇,我們可以讓一個(gè)Operation依賴(lài)于另一個(gè)Operation知允,這樣的話(huà)盡管兩個(gè)Operation處于同一個(gè)并行隊(duì)列中,但前者會(huì)直到后者執(zhí)行完畢后再執(zhí)行
- KVO監(jiān)聽(tīng)NSOperation的完成取消執(zhí)行的情況叙谨。
- 在NSOperation中温鸽,我們能夠設(shè)置任務(wù)的優(yōu)先級(jí)。GCD只能設(shè)計(jì)隊(duì)列的優(yōu)先級(jí)手负。
- 我們可以寫(xiě)一個(gè)自定義的類(lèi)繼承自NSOperation 提高代碼的復(fù)用度涤垫。
如何設(shè)計(jì)圖片緩存
- 提供相應(yīng)速度
- 節(jié)省流量
- 提高用戶(hù)體驗(yàn)。
提高響應(yīng)速度:因?yàn)閳D片一旦緩存在本地之后虫溜,那么本地IO數(shù)據(jù)的讀取雹姊,遠(yuǎn)比網(wǎng)絡(luò)中得IO讀取效率要高的多。所以可以提高響應(yīng)速度
節(jié)省流量: 一張圖片在某些情況下衡楞,只加載一次吱雏,之后便不會(huì)重新加載,減少了網(wǎng)絡(luò)流量瘾境。減少流量肯定是必然的歧杏。介于國(guó)內(nèi)的流量費(fèi)用這么貴,是肯定必要的
提高用戶(hù)體驗(yàn): 本地存儲(chǔ)迷守。
如何設(shè)計(jì)一個(gè)網(wǎng)絡(luò)控件
交互方式
顯示樣式
數(shù)據(jù)使用
-
選擇正確的初始化方式
- 分別在awakeFromNib和initWithFrame分別調(diào)用setUp
-
調(diào)整布局的時(shí)機(jī)
- frame的話(huà)在layoutSubView/viewDidLayoutSubView時(shí)更改
- 如果使用AutoLayout可以直接寫(xiě)
-
正確的使用touches方法
- 盡量使用手勢(shì)
-
drawRect和CALayer的使用與動(dòng)畫(huà)
- 繪制完成之后調(diào)用setNeedDisplay完成繪制
UIControl與UIButton
更友好的支持xib
IBInspectable 犬绒。IB_DESIGNABLE方便xib可視化-
不規(guī)范圖形和事件的觸摸范圍
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
-
合理使用KVO
- 自定義控件內(nèi)部使用KVO,監(jiān)聽(tīng)自身各種屬性的變化