來(lái)自網(wǎng)絡(luò)并淋,筆記自用
關(guān)于面試題
打個(gè)比方,如果把找工作理解成考大學(xué)哑姚,面試就是高考品山,市面上的“真題”就是模擬試卷魄懂。我們會(huì)很容易傾向于在面試前尋找對(duì)應(yīng)公司的面試“真題”篡腌,重點(diǎn)準(zhǔn)備限匣,期待“押題”成功。但實(shí)際上商膊,即使面試同一家公司昆庇,它會(huì)有不同部門府蛇,不同業(yè)務(wù)線吸耿,不同面試官,即使遇到同一面試官坟乾,他也不一定就每次考察完全一樣的內(nèi)容殷绍。想想高考中那些考的好的同學(xué)伯铣,他們肯定不是靠“押題”才能取得好成績(jī)吧却舀,他們大多靠的是平常積累及對(duì)知識(shí)點(diǎn)靈活掌握猾编,那面試也一樣啊。執(zhí)著于搜題袖迎,把面試題當(dāng)做重點(diǎn)進(jìn)行“復(fù)習(xí)”歉井,還不如自己劃出“考綱”,各個(gè)知識(shí)點(diǎn)逐一檢查掌握情況肠鲫,復(fù)習(xí)的更全面呢。
我對(duì)于面試題的看法一直是相對(duì)保守的禾乘,這類文章一般只是內(nèi)容搬運(yùn)系宫,它會(huì)存在一些偏差和誤讀领斥,最重要的那就是幾道題往那一扔狮荔,并沒(méi)有產(chǎn)出有價(jià)值的東西纲堵。這也是為什么我上篇面試總結(jié)巡雨,會(huì)加了一些面試技巧,整理面試題時(shí)席函,也沒(méi)提他們是出自哪家公司铐望,就是不希望大家把題目區(qū)別看待。
說(shuō)了這些并不是說(shuō)面試題沒(méi)用啊茂附,而是希望大家不要迷信面試題正蛙,更多地去關(guān)注那些有質(zhì)量有深度的技術(shù)文章。面試考核的是知識(shí)點(diǎn)而不是具體的某些題目营曼,面試題的作用在于乒验,衡量我們的知識(shí)掌握情況,便于我們查漏補(bǔ)缺蒂阱,越說(shuō)越像是針對(duì)一次“考試”了??锻全。
面試題及參考答案
Swift
1、Swift中struct和class有什么區(qū)別录煤?
struct是值引用鳄厌,更輕量,存放于棧區(qū)妈踊,class是類型引用了嚎,存放于堆區(qū)。struct無(wú)法繼承廊营,class可繼承歪泳。
2、Swift中的方法調(diào)用有哪些形式赘风?
答:直接派發(fā)夹囚、函數(shù)表派發(fā)、消息機(jī)制派發(fā)邀窃。派發(fā)方式受聲明位置荸哟,引用類型,特定行為的影響瞬捕。為什么Swift有這么多派發(fā)形式鞍历?為了效率。
3肪虎、Swift和OC有什么區(qū)別劣砍?
Swift和OC的區(qū)別有很多,這里簡(jiǎn)要總結(jié)這幾條:
Swift | Objective-C | |
---|---|---|
語(yǔ)言特性 | 靜態(tài)語(yǔ)言扇救,更加安全 | 動(dòng)態(tài)語(yǔ)言刑枝,不那么安全 |
語(yǔ)法 | 更精簡(jiǎn) | 冗長(zhǎng) |
命名空間 | 有 | 無(wú) |
方法調(diào)用 | 直接調(diào)用香嗓,函數(shù)表調(diào)用,消息轉(zhuǎn)發(fā) | 消息轉(zhuǎn)發(fā) |
泛型/元組/高階函數(shù) | 有 | 無(wú) |
語(yǔ)言效率 | 性能更高装畅,速度更快 | 略低 |
文件特性 | .swift 單文件 | .h/.m包含頭文件 |
編程特性 | 可以更好的實(shí)現(xiàn)函數(shù)式編程/響應(yīng)式編程 | 面向?qū)ο缶幊?/td> |
4靠娱、從OC向Swift遷移的時(shí)候遇到過(guò)什么問(wèn)題?
可以參考這篇文章:OC項(xiàng)目轉(zhuǎn)Swift指南 里的混編注意事項(xiàng)掠兄。
5像云、怎么理解面向協(xié)議編程?
面向?qū)ο笫且詫?duì)象的視角觀察整體結(jié)構(gòu)蚂夕,萬(wàn)物皆為對(duì)象迅诬。
面向協(xié)議則是用協(xié)議的方式組織各個(gè)類的關(guān)系,Swift底層幾乎所有類都構(gòu)建在協(xié)議之上婿牍。
面向協(xié)議能夠解決面向?qū)ο蟮牧庑卫^承侈贷,橫切關(guān)注點(diǎn)和動(dòng)態(tài)派發(fā)的安全性等問(wèn)題。
參考喵神的面向協(xié)議編程與 Cocoa 的邂逅 (上)
OC語(yǔ)法
1牍汹、Block是如何實(shí)現(xiàn)的铐维?Block對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)是什么樣子的柬泽?__block的作用是什么慎菲?它對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)又是什么樣子的?
block本質(zhì)是一個(gè)對(duì)象锨并,底層用struct實(shí)現(xiàn)露该。
數(shù)據(jù)結(jié)構(gòu)如下:
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
isa 指針,所有對(duì)象都有該指針第煮,用于實(shí)現(xiàn)對(duì)象相關(guān)的功能解幼。
flags,用于按 bit 位表示一些 block 的附加信息包警,本文后面介紹 block copy 的實(shí)現(xiàn)代碼可以看到對(duì)該變量的使用撵摆。
reserved,保留變量害晦。
invoke特铝,函數(shù)指針,指向具體的 block 實(shí)現(xiàn)的函數(shù)調(diào)用地址壹瘟。
descriptor鲫剿, 表示該 block 的附加描述信息,主要是 size 大小稻轨,以及 copy 和 dispose 函數(shù)的指針灵莲。
variables,capture 過(guò)來(lái)的變量殴俱,block 能夠訪問(wèn)它外部的局部變量政冻,就是因?yàn)閷⑦@些變量(或變量的地址)復(fù)制到了結(jié)構(gòu)體中枚抵。
__block
的作用是可以獲取對(duì)應(yīng)變量的指針,使其可以在block內(nèi)部被修改明场。通過(guò)反編譯的代碼我們可以看到該對(duì)象是這樣的:
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int val; //變量名
};
對(duì)于block的深入了解俄精,可以參考《Objective-C高級(jí)編程》第二章或者唐巧的這篇談Objective-C block的實(shí)現(xiàn)
2、GCD中的Block是在堆上還是棧上榕堰?
堆上竖慧。可以通過(guò)block的isa指針確認(rèn)逆屡。
3圾旨、NSCoding協(xié)議是干什么用的?
一種編碼協(xié)議魏蔗,歸檔時(shí)和解檔時(shí)需要依賴該協(xié)議定義的編碼和解碼方法砍的。Foundation和Cocoa Touch中的大部分類都遵循了這個(gè)協(xié)議,一般被NSKeyedArchiver做自定義對(duì)象持久化時(shí)使用莺治。
4廓鞠、KVO的實(shí)現(xiàn)原理
利用Runtime生成一個(gè)中間對(duì)象,讓原對(duì)象的isa指針指向它谣旁,然后重寫setter方法床佳,插入willChangeValueForKey和didChangeValueForKey方法。當(dāng)屬性變化時(shí)會(huì)調(diào)用榄审,會(huì)調(diào)用這兩個(gè)方法通知到外界屬性變化砌们。
5、NSOperation有哪些特性搁进,比著GCD有哪些優(yōu)點(diǎn)浪感,它有哪些API?
NSOperation是對(duì)GCD的封裝饼问,具有面向?qū)ο蟮奶攸c(diǎn)影兽,可以更方便的進(jìn)行封裝,可以設(shè)置依賴關(guān)系莱革。
API可以查看NSOperation文檔峻堰。
6、NSNotificaiton是同步還是異步的驮吱,如果發(fā)通知時(shí)在子線程茧妒,接收在哪個(gè)線程?
同步左冬。子線程桐筏。
UI
1、事件響應(yīng)鏈?zhǔn)侨绾蝹鬟f的拇砰?
手勢(shì)的點(diǎn)擊會(huì)發(fā)生兩個(gè)重要事情梅忌,事件傳遞和事件響應(yīng)狰腌。
事件傳遞:從UIApplication開始,到window牧氮,再逐步往下層(子視圖)找琼腔,直到找到最深層的子視圖,其為first responder踱葛。用到的判斷方法是pointInside:withEvent
和hitTest:withEvent
丹莲。
事件響應(yīng):從識(shí)別到的視圖(first responder)開始驗(yàn)證能否響應(yīng)事件,如果不能就交給其上層(父視圖)視圖尸诽,如果能相應(yīng)將不再往下傳遞甥材,如果直到找到UIApplication層還沒(méi)有相應(yīng),那就忽略該次點(diǎn)擊性含。用到的判斷方法是touchesBegan:withEvent
洲赵、touchesMoved:withEvent
等。
這兩個(gè)過(guò)程大致的相反的商蕴。
2叠萍、什么是異步渲染?
異步渲染就是在子線程進(jìn)行繪制绪商,然后拿到主線程顯示苛谷。
UIView的顯示是通過(guò)CALayer實(shí)現(xiàn)的,CALayer的顯示則是通過(guò)contents進(jìn)行的部宿。異步渲染的實(shí)現(xiàn)原理是當(dāng)我們改變UIView的frame時(shí)抄腔,會(huì)調(diào)用layer的setNeedsDisplay,然后調(diào)用layer的display方法理张。我們不能在非主線程將內(nèi)容繪制到layer的context上,但我們單獨(dú)開一個(gè)子線程通過(guò)CGBitmapContextCreateImage()
繪制內(nèi)容绵患,繪制完成之后切回主線程雾叭,將內(nèi)容賦值到contents上。
這個(gè)步驟可以參照YYText中YYTextAsyncLayer.m文件中的實(shí)現(xiàn)方式落蝙。
3织狐、layoutsubviews是在什么時(shí)機(jī)調(diào)用的?
init初始化不會(huì)觸發(fā)筏勒。
addSubview時(shí)移迫。
設(shè)置frame且前后值變化,frame為zero且不添加到指定視圖不會(huì)觸發(fā)管行。
旋轉(zhuǎn)Screen會(huì)觸發(fā)父視圖的layoutSubviews厨埋。
滾動(dòng)UIScrollView引起View重新布局時(shí)會(huì)觸發(fā)layoutSubviews。
4捐顷、一張圖片的展示經(jīng)歷了哪些步驟荡陷?
這個(gè)可以參考我之前寫的一篇文章iOS開發(fā)圖片格式選擇 中的前半部分內(nèi)容雨效。
5、什么是離屏渲染废赞,什么情況會(huì)導(dǎo)致離屏渲染徽龟?
如果要在顯示屏上顯示內(nèi)容,我們至少需要一塊與屏幕像素?cái)?shù)據(jù)量一樣大的frame buffer唉地,作為像素?cái)?shù)據(jù)存儲(chǔ)區(qū)域据悔。如果有時(shí)因?yàn)槊媾R一些限制,無(wú)法把渲染結(jié)果直接寫入frame buffer耘沼,而是先暫存在另外的內(nèi)存區(qū)域屠尊,之后再寫入frame buffer,那么這個(gè)過(guò)程被稱之為離屏渲染耕拷。
以陰影為例讼昆,為什么它會(huì)導(dǎo)致離屏渲染。因?yàn)镚PU的渲染是遵循“畫家算法”骚烧,一層一層繪制的浸赫,但陰影很特殊,它需要全部?jī)?nèi)容繪制完成赃绊,再根據(jù)外輪廓進(jìn)行繪制既峡。這就導(dǎo)致了,陰影這一層要一直占據(jù)一塊內(nèi)存區(qū)域碧查,這就導(dǎo)致了離屏渲染运敢。
類似導(dǎo)致離屏渲染的情況還有:
- cornerRadius+clipsToBounds
- group opacity 組透明度
- mask 遮罩
- UIBlurEffect 毛玻璃效果
有一篇文章詳細(xì)的討論了這些情況:關(guān)于iOS離屏渲染的深入研究
6、CoreAnimation這個(gè)框架的作用什么忠售,它跟UIKit的關(guān)系是什么传惠?
CoreAnimation雖然直譯是核心動(dòng)畫,但它其實(shí)是一個(gè)圖像渲染框架稻扬,動(dòng)畫實(shí)現(xiàn)只是它的一部分功能卦方。
看這張圖我們可以知道,它是UIKit和AppKit的底層實(shí)現(xiàn)泰佳,位于Metal盼砍、Core Graphics和GPU之上之上。
蘋果官方文檔:About Core Animation
引用計(jì)數(shù)
1逝她、ARC方案的原理是什么浇坐?它是在什么時(shí)候做的隱式添加release操作?
ARC(Automatic Reference Cunting)自動(dòng)引用計(jì)數(shù)黔宛,意即通過(guò)LLVM編譯器自動(dòng)管理對(duì)應(yīng)的引用計(jì)數(shù)狀態(tài)近刘。ARC開啟時(shí)無(wú)需再次鍵入retain或者release代碼。
它是在編譯階段添加retain或者release代碼的。
2跌宛、循環(huán)引用有哪些場(chǎng)景酗宋,如何避免?
循環(huán)引用及兩個(gè)及以上對(duì)象出現(xiàn)引用環(huán)疆拘,導(dǎo)致對(duì)象無(wú)法釋放的情況蜕猫。一般在block,delegate哎迄,NSTimer時(shí)容易出現(xiàn)這個(gè)問(wèn)題回右。
解決方案就是讓環(huán)的其中一環(huán)節(jié)實(shí)現(xiàn)弱引用。
3漱挚、為什么當(dāng)我們?cè)谑褂胋lock時(shí)外面是weak 聲明一個(gè)weakSelf翔烁,還要在block內(nèi)部使用strong再持有一下?
block外界聲明weak是為了實(shí)現(xiàn)block對(duì)對(duì)象的弱持有旨涝,而里面的作用是為了保證在進(jìn)到block時(shí)不會(huì)發(fā)生釋放蹬屹。
4、Autoreleasepool是實(shí)現(xiàn)機(jī)制是什么白华?它是什么時(shí)候釋放內(nèi)部的對(duì)象的慨默?它內(nèi)部的數(shù)據(jù)結(jié)構(gòu)是什么樣的?當(dāng)我提到哨兵對(duì)象時(shí)弧腥,會(huì)繼續(xù)問(wèn)哨兵對(duì)象的作用是什么厦取,為什么要設(shè)計(jì)它?
Autoreleasepool的原理是一個(gè)雙向列表管搪,它會(huì)對(duì)加入其中的對(duì)象實(shí)現(xiàn)延遲釋放虾攻。當(dāng)Autoreleasepool調(diào)用drain方法時(shí)會(huì)釋放內(nèi)部標(biāo)記為autorelease的對(duì)象。
class AutoreleasePoolPage {
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
哨兵對(duì)象類似一個(gè)指針更鲁,指向自動(dòng)釋放池的棧頂位置霎箍,它的作用就是用于標(biāo)記當(dāng)前自動(dòng)釋放池需要釋放內(nèi)部對(duì)象時(shí),釋放到那個(gè)地方結(jié)束岁经,每次入棧時(shí)它用于確定添加的位置朋沮,然后再次移動(dòng)到棧頂。
關(guān)于自動(dòng)釋放池的底層探究可以看draveness的這篇自動(dòng)釋放池的前世今生 ---- 深入解析 autoreleasepool
5缀壤、哪些對(duì)象會(huì)放入到Autoreleasepool中?
有兩種情況生成的對(duì)象會(huì)加入到autoreleasepool中:
- 非alloc/new/copy/mutablecopy 開始的方式初始化時(shí)纠亚。
- id的指針或?qū)ο蟮闹羔樤跊](méi)有顯示指定時(shí)
6塘慕、weak的實(shí)現(xiàn)原理是什么?當(dāng)引用對(duì)象銷毀是它是如何管理內(nèi)部的Hash表的蒂胞?(這里要參閱weak源碼)
runTime會(huì)把對(duì)weak修飾的對(duì)象放到一個(gè)全局的哈希表中图呢,用weak修飾的對(duì)象的內(nèi)存地址為key,weak指針為值,在對(duì)象進(jìn)行銷毀時(shí)蛤织,用通過(guò)自身地址去哈希表中查找到所有指向此對(duì)象的weak指針赴叹,并把所有的weak指針置位nil。
Runtime
1指蚜、消息發(fā)送的流程是怎樣的乞巧?
OC中的方法調(diào)用會(huì)轉(zhuǎn)化成給對(duì)象發(fā)送消息,發(fā)送消息會(huì)調(diào)用這個(gè)方法:
objc_msgSend(receiver, @selector(message))
該過(guò)程有以下關(guān)鍵步驟:
先確定調(diào)用方法的類已經(jīng)都加載完畢摊鸡,如果沒(méi)加載完畢的話進(jìn)行加載
從cache中查找方法
cache中沒(méi)有找到對(duì)應(yīng)的方法绽媒,則到方法列表中查,查到則緩存
如果本類中查詢到?jīng)]有結(jié)果免猾,則遍歷所有父類重復(fù)上面的查找過(guò)程是辕,直到NSObject
2、關(guān)聯(lián)對(duì)象時(shí)什么情況下會(huì)導(dǎo)致內(nèi)存泄露猎提?
關(guān)聯(lián)對(duì)象可以理解就是持有了一個(gè)對(duì)象获三,如果是retain等方式的持有,而該對(duì)象也持有了本類锨苏,那就是導(dǎo)致了循環(huán)引用疙教。
3、消息轉(zhuǎn)發(fā)的流程是什么蚓炬?
消息轉(zhuǎn)發(fā)是發(fā)生在接收者(receiver)沒(méi)有找到對(duì)應(yīng)的方法(method)的時(shí)候松逊,該步驟有如下幾個(gè)關(guān)鍵步驟:
- 消息轉(zhuǎn)發(fā)的時(shí)候,如果是實(shí)例方法會(huì)走
resolveInstanceMethod:
肯夏,如果是類方法會(huì)走resolveClassMethod:
经宏,它們的返回值都是Bool,需要我們確定是否進(jìn)行轉(zhuǎn)發(fā)驯击。 - 如果第一步返回YES烁兰,確定轉(zhuǎn)發(fā)就會(huì)進(jìn)到下個(gè)方法
forwardingTargetForSelector
,這個(gè)方法需要我們指定一個(gè)被用receiver徊都。 -
methodSignatureForSelector
用于指定方法簽名沪斟,forwardInvocation
用于處理Invocation,進(jìn)行完整轉(zhuǎn)發(fā)暇矫。 - 如果消息轉(zhuǎn)發(fā)也沒(méi)有處理即為無(wú)法處理主之,會(huì)調(diào)用
doesNotRecognizeSelector
,引發(fā)崩潰李根。
4房轿、category能否添加屬性粤攒,為什么所森?能否添加實(shí)例變量,為什么夯接?
可以添加屬性焕济,這里的屬性指@property
,但跟類里的@property
又不一樣盔几。正常的@property
為:實(shí)例變量Ivar + Setter + Getter
方法晴弃,分類里的@property
這三者都沒(méi)有,需要我們手動(dòng)實(shí)現(xiàn)问欠。
分類是運(yùn)行時(shí)被編譯的肝匆,這時(shí)類的結(jié)構(gòu)已經(jīng)固定了,所以我們無(wú)法添加實(shí)例變量顺献。
對(duì)于分類自定義Setter和Getter方法旗国,我們可以通過(guò)關(guān)聯(lián)對(duì)象(Associated Object)進(jìn)行實(shí)現(xiàn)。
5注整、元類的作用是什么能曾?
元類的作用是存儲(chǔ)類方法,同時(shí)它也是為了讓OC的類結(jié)構(gòu)能夠形成閉環(huán)肿轨。
對(duì)于為甚設(shè)計(jì)元類有以下原因寿冕;
在OC的世界里一切皆對(duì)象(借鑒于Smalltalk),
metaclass
的設(shè)計(jì)就是要為滿足這一點(diǎn)椒袍。在OC中Class也是一種對(duì)象驼唱,它對(duì)應(yīng)的類就是
metaclass
,metaclass
也是一種對(duì)象驹暑,它的類是rootmetaclass
玫恳,在往上根元類(root metaclass)指向自己,形成了一個(gè)閉環(huán)优俘,一個(gè)完備的設(shè)計(jì)京办。
如果不要metaclass可不可以?也是可以的帆焕,在objc_class
再加一個(gè)類方法指針惭婿。但是這樣的設(shè)計(jì)會(huì)將消息傳遞的過(guò)程復(fù)雜化,所以為了消息傳遞流程的復(fù)用叶雹,為了一切皆對(duì)象的思想财饥,就有了metaclass。
關(guān)于這一話題的深入討論可以參考這兩篇文章:
6折晦、類方法是存儲(chǔ)到什么地方的佑力?類屬性呢?
類方法和類屬性都是存儲(chǔ)到元類中的筋遭。
類屬性在Swift用的多些,OC中很少有人用到,但其實(shí)它也是有的漓滔,寫法如下:
@interface Person : NSObject
// 在屬性類別中加上class
@property (class, nonatomic, copy) NSString *name;
@end
// 調(diào)用方式
NSString *temp = Person.name;
需要注意的是跟實(shí)例屬性不一樣编饺,類屬性不會(huì)自動(dòng)生成實(shí)例變量和setter,getter方法响驴,需要我們手動(dòng)實(shí)現(xiàn)透且。具體實(shí)現(xiàn)方法可以參考這個(gè)文章:Objective-C Class Properties
7、講幾個(gè)runtime的應(yīng)用場(chǎng)景
- hook系統(tǒng)方法進(jìn)行方法交換豁鲤。
- 了解一個(gè)類(閉源)的私有屬性和方法秽誊。
- 關(guān)聯(lián)對(duì)象,實(shí)現(xiàn)添加分類屬性的功能琳骡。
- 修改isa指針锅论,自定義KVO。
Runloop
1楣号、講一下對(duì)Runloop的理解最易?
Runloop就是一個(gè)運(yùn)行循環(huán),它保證了在沒(méi)有任務(wù)的時(shí)候線程不退出炫狱,有任務(wù)的時(shí)候即使響應(yīng)藻懒。Runloop跟線程,事件響應(yīng)视译,手勢(shì)識(shí)別嬉荆,頁(yè)面更新,定時(shí)器都有著緊密聯(lián)系酷含。
深入了解推薦ibireme的這篇深入理解RunLoop
2鄙早、可以用Runloop實(shí)現(xiàn)什么功能?
- 檢測(cè)卡頓
- 線程钡诿溃活
- 性能優(yōu)化蝶锋,將一些耗時(shí)操作放到runloop wait的情況處理。
性能優(yōu)化
1什往、對(duì)TableView進(jìn)行性能優(yōu)化有哪些方式扳缕?
- 緩存高度
- 異步渲染
- 減少離屏渲染
2、Xcode的Instruments都有哪些調(diào)試的工具别威?
-
Activity Monitor(活動(dòng)監(jiān)視器):監(jiān)控進(jìn)程的CPU躯舔、內(nèi)存、磁盤省古、網(wǎng)絡(luò)使用情況粥庄。是程序在手機(jī)
運(yùn)行真正占用內(nèi)存大小
Allocations(內(nèi)存分配):跟蹤過(guò)程的匿名虛擬內(nèi)存和堆的對(duì)象提供類名和可選保留/釋放歷史
Core Animation(圖形性能):顯示程序顯卡性能以及CPU使用情況
Core Data:跟蹤C(jī)ore Data文件系統(tǒng)活動(dòng)
Energy Log:耗電量監(jiān)控
File Activity:檢測(cè)文件創(chuàng)建、移動(dòng)豺妓、變化惜互、刪除等
Leaks(泄漏):一般的措施內(nèi)存使用情況布讹,檢查泄漏的內(nèi)存,并提供了所有活動(dòng)的分配和泄漏模塊的類對(duì)象分配統(tǒng)計(jì)信息以及內(nèi)存地址歷史記錄
Network:用鏈接工具分析你的程序如何使用TCP/IP和UDP/IP鏈接
System Usage:記錄關(guān)于文件讀寫训堆,sockets描验,I/O系統(tǒng)活動(dòng),輸入輸出
Time Profiler(時(shí)間探查):方法執(zhí)行耗時(shí)分析
Zombies:測(cè)量一般的內(nèi)存使用坑鱼,專注于檢測(cè)過(guò)度釋放的野指針對(duì)象膘流。也提供對(duì)象分配統(tǒng)計(jì)以及主動(dòng)分配的內(nèi)存地址歷史
3、講一下你做過(guò)的性能優(yōu)化的事情鲁沥。
這個(gè)根據(jù)自己情況來(lái)說(shuō)吧呼股。
4、如何檢測(cè)卡頓画恰,都有哪些方法彭谁?
- FPS,通過(guò)
CADisplayLink
計(jì)算1s內(nèi)刷新次數(shù)阐枣,也可以利用Instruments里的Core Animation马靠。 - 利用Runloop,實(shí)時(shí)計(jì)算
kCFRunLoopBeforeSources
和kCFRunLoopAfterWaiting
兩個(gè)狀態(tài)區(qū)域之間的耗時(shí)是否超過(guò)某個(gè)閥值 - 子線程檢測(cè)蔼两,每次檢測(cè)時(shí)設(shè)置標(biāo)記位為YES甩鳄,然后派發(fā)任務(wù)到主線程中將標(biāo)記位設(shè)置為NO。接著子線程沉睡超時(shí)闕值時(shí)長(zhǎng)额划,判斷標(biāo)志位是否成功設(shè)置成NO妙啃,如果沒(méi)有說(shuō)明主線程發(fā)生了卡頓。參考ANREye的實(shí)現(xiàn)
5俊戳、縮小包體積有哪些方案揖赴?
- 圖片壓縮,無(wú)用圖片刪除
- 一些大圖可以動(dòng)態(tài)下發(fā)
- 刪除無(wú)用類抑胎,無(wú)用方法
- 減少三方庫(kù)的依賴
計(jì)算機(jī)相關(guān)
1燥滑、項(xiàng)目編譯的流程是什么?手機(jī)上的應(yīng)用程序自點(diǎn)擊圖標(biāo)開始到首屏內(nèi)容展示都經(jīng)歷了哪些步驟阿逃?
編譯流程:
預(yù)處理:處理宏定義铭拧,刪除注釋,展開頭文件恃锉。
詞法分析:把代碼切成一個(gè)個(gè)token搀菩,比如大小括號(hào)等于號(hào)還有字符串
語(yǔ)法分析:驗(yàn)證語(yǔ)法是否正確,合成抽象語(yǔ)法樹AST
靜態(tài)分析:查找代碼錯(cuò)誤
類型檢查:動(dòng)態(tài)和靜態(tài)
目標(biāo)代碼的生成與優(yōu)化破托,包括刪除多余指令肪跋,選擇合適的尋址方式,如果開啟了bitcode土砂,會(huì)做進(jìn)一步的優(yōu)化
匯編:由匯編器生成匯編語(yǔ)言
機(jī)器碼:由匯編語(yǔ)言轉(zhuǎn)成機(jī)器碼州既,生成.o文件
應(yīng)用啟動(dòng)的流程:
啟動(dòng)的前提是完成編譯谜洽,運(yùn)行程序即運(yùn)行編譯過(guò)后的目標(biāo)程序,它分為main函數(shù)前和main函數(shù)后:
main前
加載可執(zhí)行文件(App的.o文件集合)
加載動(dòng)態(tài)鏈接庫(kù)(系統(tǒng)和應(yīng)用的動(dòng)態(tài)鏈接庫(kù))易桃,進(jìn)行rebase指針調(diào)整和bind符號(hào)綁定
Objc運(yùn)行時(shí)的初始處理震肮,包括Objc相關(guān)類的注冊(cè)赴恨,category注冊(cè),selector唯一性檢查
初始化袍辞,包括執(zhí)行+load()贸宏、attribute(constructor)修飾的函數(shù)的調(diào)用造寝、創(chuàng)建C++靜態(tài)全局變量
main后
首頁(yè)初始化所需要配置文件的讀寫操作
首頁(yè)界面渲染
2、對(duì)于基本數(shù)據(jù)類型吭练,一般是存儲(chǔ)到棧中的诫龙,它有沒(méi)有可能存在堆上,什么情況下會(huì)存儲(chǔ)到堆上鲫咽?
棧和堆都是同屬一塊內(nèi)存签赃,只不過(guò)一個(gè)是高地址往低地址存儲(chǔ),一個(gè)從低地址往高地址存儲(chǔ)分尸,他們并沒(méi)有嚴(yán)格的界限說(shuō)一個(gè)值只能放在堆上或者棧上锦聊。所以基本數(shù)據(jù)類型也是可以存儲(chǔ)到堆上的。
當(dāng)該基礎(chǔ)類型變量被__block
捕獲時(shí)箩绍,該變量連同block都會(huì)被copy到堆上孔庭。
3、數(shù)據(jù)庫(kù)中的事務(wù)是什么意思材蛛?
事務(wù)就是訪問(wèn)并操作各種數(shù)據(jù)項(xiàng)的一個(gè)數(shù)據(jù)庫(kù)操作序列圆到,這些操作要么全部執(zhí)行,要么全部不執(zhí)行卑吭。如果其中一個(gè)步驟出錯(cuò)就要撤銷整個(gè)操作芽淡,回滾到進(jìn)入事務(wù)之前的狀態(tài)。
4豆赏、使用過(guò)什么數(shù)據(jù)庫(kù)(我回答的Sqlite挣菲,Realm),Realm在使用時(shí)有哪些注意事項(xiàng)河绽,如何實(shí)現(xiàn)批量操作己单?
對(duì)于Realm感興趣的同學(xué)可以看下其官方文檔。
Realm需要注意的主要就是不能直接跨線程訪問(wèn)同一對(duì)象耙饰。
批量操作可以在一個(gè)單獨(dú)的事務(wù)中執(zhí)行多個(gè)數(shù)據(jù)庫(kù)的修改纹笼。
5、LRU算法是否了解苟跪,如何實(shí)現(xiàn)一套LRU算法廷痘?
LRU(Least recently used 最近最少使用)算法是一個(gè)緩存淘汰算法蔓涧,其作用就是當(dāng)緩存很多時(shí),該淘汰哪些內(nèi)容笋额,見名知意元暴,它的核心思想是淘汰最近使用最少的內(nèi)容。實(shí)現(xiàn)它的關(guān)鍵步驟是:
新數(shù)據(jù)插入到鏈表的頭部
每當(dāng)緩存命中時(shí)兄猩,則將數(shù)據(jù)移動(dòng)到鏈表頭部
鏈表滿時(shí)茉盏,將尾部數(shù)據(jù)清除
這個(gè)算法在SDWebImage和Kingfisher等需要處理緩存的庫(kù)中都有實(shí)現(xiàn)。
6枢冤、知道哪些設(shè)計(jì)模式鸠姨,怎么理解設(shè)計(jì)模式的作用?
工廠模式淹真、觀察者模式讶迁、中介者模式、單例模式核蘸。這個(gè)根據(jù)實(shí)際情況說(shuō)吧巍糯。
7、如果有1000萬(wàn)個(gè)Int類型的數(shù)字客扎,如何對(duì)他們排序祟峦?
這里的隱藏含義是,內(nèi)存不夠用時(shí)如何排序虐唠,還有一個(gè)隱藏含義是硬盤足夠大搀愧。這是可以采用分而治之的方法,將數(shù)據(jù)分成若干塊疆偿,使每一小塊滿足當(dāng)前內(nèi)容大小咱筛,然后對(duì)每塊內(nèi)容單獨(dú)排序,最后采用歸并排序?qū)λ袎K進(jìn)行排序杆故,就得到了一個(gè)有序序列迅箩。
8、設(shè)計(jì)一套數(shù)據(jù)庫(kù)方案处铛,實(shí)現(xiàn)類似微信的搜索關(guān)鍵詞能快速檢索出包含該字符串的聊天信息饲趋,并展示對(duì)應(yīng)數(shù)量(聊天記錄的數(shù)據(jù)量較大)
可以對(duì)聊天記錄的文本值加上索引。正常情況下數(shù)據(jù)庫(kù)搜索都是全量檢索的撤蟆,加上索引之后只會(huì)檢索滿足條件的記錄奕塑,大大降低檢索量。
簡(jiǎn)歷相關(guān)問(wèn)題
1家肯、Lottie實(shí)現(xiàn)動(dòng)畫效果的原理是什么龄砰?
iOS里的動(dòng)畫基本都是基于CoreAnimation里的API實(shí)現(xiàn)的,Lottie也是如此。在AE上實(shí)現(xiàn)動(dòng)畫效果换棚,通過(guò)插件導(dǎo)出對(duì)應(yīng)的json文件式镐,Lottie的庫(kù)解析該json,轉(zhuǎn)成對(duì)應(yīng)的系統(tǒng)API方法固蚤。圖片的引用可以使用Base64編到j(luò)son里娘汞,也可以通過(guò)項(xiàng)目集成,通過(guò)路徑引用夕玩。
2你弦、OClint實(shí)現(xiàn)靜態(tài)分析的原理是什么,它是如何做到的风秤?
具體可以參考我之前寫的如何通過(guò)靜態(tài)分析提高iOS代碼質(zhì)量鳖目。
3、MVVM和MVC有什么區(qū)別缤弦?
對(duì)比架構(gòu)時(shí),可以從是否職責(zé)分離彻磁,可測(cè)試性碍沐,可易維護(hù)性三個(gè)維度對(duì)比。
更多對(duì)比可以參考我翻譯的一篇文章:【譯】iOS 架構(gòu)模式--淺析MVC, MVP, MVVM 和 VIPER
4衷蜓、靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別是什么累提?
靜態(tài)庫(kù):鏈接時(shí)被完整復(fù)制到可執(zhí)行文件中,多次使用就多份拷貝磁浇。
動(dòng)態(tài)庫(kù):鏈接時(shí)不復(fù)制斋陪,而是由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,內(nèi)存中只會(huì)有一份該動(dòng)態(tài)庫(kù)置吓。
5无虚、了解Flutter嗎?它有沒(méi)有使用UIKit衍锚?它是如何渲染UI的友题?
UIKit是基于CoreAnimation渲染的,而Flutter并沒(méi)有用到它戴质,而是自己基于C++實(shí)現(xiàn)了一套渲染框架度宦。
6、二進(jìn)制重排的核心依據(jù)是什么告匠?
修改鏈接順序戈抄,減少啟動(dòng)時(shí)的缺頁(yè)中斷。
實(shí)踐步驟可以參考李斌同學(xué)的這篇iOS 優(yōu)化篇 - 啟動(dòng)優(yōu)化之Clang插樁實(shí)現(xiàn)二進(jìn)制重排
7后专、如何設(shè)計(jì)一套切換主題的方案划鸽?
核心思路是觀察者模式+協(xié)議(通知),當(dāng)獲取到主題切換時(shí)行贪,通知各個(gè)實(shí)現(xiàn)了主題協(xié)議的類進(jìn)行更新漾稀。
8模闲、AVPlayer和IJKPlayer有什么區(qū)別?用IJKPlayer如何實(shí)現(xiàn)一個(gè)緩存視頻列表每條視頻前1s的內(nèi)容崭捍?
因?yàn)閷?duì)IJKPlayer和FFmpeg了解的不是很深尸折,這個(gè)我也沒(méi)有確切答案,如果有了解的小伙伴可以評(píng)論告知我殷蛇。
9实夹、類似微博的短視頻列表,滑動(dòng)停留播放粒梦,如何實(shí)現(xiàn)亮航?
這個(gè)主要就是檢測(cè)contentOffset和屏幕中間位置,設(shè)置一些邊界條件匀们,處理滑動(dòng)過(guò)程中的切換行為缴淋。
10、使用python做過(guò)哪些事泄朴?如何理解腳本語(yǔ)言重抖?
多語(yǔ)言管理,csv多語(yǔ)言文件讀取祖灰,然后寫入到項(xiàng)目Localizable.strings中钟沛;抓取項(xiàng)目中的多語(yǔ)言字符串。
腳本(script) 其實(shí)就是一系列指令局扶,計(jì)算機(jī)看了指令就知道自己該做什么事情恨统。像常見的Python,Shell三妈,Ruby都是腳本語(yǔ)言畜埋,他們通常不需要編譯,通過(guò)解釋器運(yùn)行沈跨。
數(shù)據(jù)結(jié)構(gòu)與算法
1由捎、什么是Hash表,什么是Hash碰撞饿凛,解決Hash碰撞有什么方法狞玛?
哈希表(Hash Table,也叫散列表)涧窒,是根據(jù)關(guān)鍵碼值 (Key-Value) 而直接進(jìn)行訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)心肪。也就是說(shuō),它通過(guò)把關(guān)鍵碼值映射到表中一個(gè)位置來(lái)訪問(wèn)記錄纠吴,以加快查找的速度硬鞍。我們常用的Dictionary就是一種Hash表。
那什么是Hash碰撞呢,我們知道Hash表的查找是通過(guò)鍵值進(jìn)行定位的固该,當(dāng)兩個(gè)不同的輸入對(duì)應(yīng)一個(gè)輸出時(shí)锅减,即為Hash碰撞,也被稱為Hash沖突伐坏。
如果使用字典的例子你可能聯(lián)想不到?jīng)_突的情況怔匣,我們假設(shè)另一種情況:假設(shè)hash表的大小為9(即有9個(gè)槽),現(xiàn)在要把一串?dāng)?shù)據(jù)存到表里:5,28,19,15,20,33,12,17,10桦沉。我們使用的hash函數(shù)是對(duì)9取余每瞒。這樣的話會(huì)出現(xiàn)hash(5)=5,hash(28)=1纯露,hash(19)=1剿骨。28和19都對(duì)應(yīng)一個(gè)地址,這就出現(xiàn)了Hash沖突埠褪。
解決Hash沖突的方式有開放定址法和鏈地址法浓利。
2、如何遍歷二叉樹钞速?
二叉樹的遍歷有三種方式荞膘,對(duì)于上面這棵二叉樹,他們的遍歷結(jié)果為:
前序遍歷:根節(jié)點(diǎn) > 左子節(jié)點(diǎn) > 右子節(jié)點(diǎn)玉工。
10,6淘菩,4遵班,8,14潮改,12狭郑,16
中序遍歷:左子節(jié)點(diǎn) > 根節(jié)點(diǎn) > 右子節(jié)點(diǎn)。
4汇在,6翰萨,8,10糕殉,12亩鬼,14,16
后序遍歷:左子節(jié)點(diǎn) > 右子節(jié)點(diǎn) > 根節(jié)點(diǎn)阿蝶。
4雳锋,8,6羡洁,12玷过,16,14,10
3辛蚊、簡(jiǎn)述下快速排序的過(guò)程粤蝎,時(shí)間復(fù)雜度是多少?
快排的思想是通過(guò)一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分袋马,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小初澎,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,整個(gè)排序過(guò)程可以遞歸進(jìn)行飞蛹。
一個(gè)簡(jiǎn)單的Swift實(shí)現(xiàn)方式如下:
func quicksort<T: Comparable>(_ a: [T]) -> [T] {
guard a.count > 1 else { return a }
let pivot = a[a.count/2]
let less = a.filter { $0 < pivot }
let equal = a.filter { $0 == pivot }
let greater = a.filter { $0 > pivot }
return quicksort(less) + equal + quicksort(greater)
}
快速排序是有好幾種的谤狡,他們的區(qū)別在于如何實(shí)現(xiàn)filter和分區(qū)基準(zhǔn)值的選取。
快排的時(shí)間復(fù)雜度是O(nlogn)
卧檐,空間復(fù)雜度是O(logn)
4墓懂、有一個(gè)整數(shù)數(shù)組,如何只遍歷一遍就實(shí)現(xiàn)讓該數(shù)組奇數(shù)都在前面霉囚,偶數(shù)都在后面捕仔?
這個(gè)是《劍指offer》里的一道題,leedcode也有對(duì)應(yīng)題目:劍指offer 21
這個(gè)相對(duì)比較簡(jiǎn)單盈罐,因?yàn)椴灰笥行虬竦梢圆捎檬瘴脖闅v的方式,進(jìn)行交換盅粪,我這有個(gè)參考答案:
func sorted( _ nums: inout [Int]) -> [Int] {
guard !nums.isEmpty else {
return []
}
var start = 0
var end = nums.count - 1
while start < end {
if nums[start] % 2 != 0 {
start += 1
continue
}
if nums[end] % 2 == 0 {
end -= 1
continue
}
(nums[start], nums[end]) = (nums[end], nums[start])
}
return nums
}
5钓葫、假設(shè)你正在爬樓梯。需要 n 階你才能到達(dá)樓頂票顾。每次你可以爬 1 或 2 個(gè)臺(tái)階础浮。你有多少種不同的方法可以爬到樓頂呢?
6奠骄、給出一個(gè) 32 位的有符號(hào)整數(shù)豆同,你需要將這個(gè)整數(shù)中每位上的數(shù)字進(jìn)行反轉(zhuǎn)
7、有紅含鳞、黃影锈、藍(lán)三種顏色的氣球。在挪醣粒客王國(guó)鸭廷,1個(gè)紅氣球+1個(gè)黃氣球+1個(gè)藍(lán)氣球可以兌換一張彩票
2個(gè)紅氣球+1個(gè)黃氣球可以兌換1個(gè)藍(lán)氣球。
2個(gè)黃氣球+1個(gè)藍(lán)氣球可以兌換1個(gè)紅氣球潜必。
2個(gè)藍(lán)氣球+1個(gè)紅氣球可以兌換1個(gè)黃氣球靴姿。
現(xiàn)在牛牛有a個(gè)紅氣球,b個(gè)黃氣球磁滚, c個(gè)藍(lán)氣球佛吓,牛牛想知道自己最多可以兌換多少?gòu)埐势薄?/p>