ios面試題2

聲明:面試是對自我審視的一種過程,面試題和iOS程序員本身技術水平沒任何關聯,無論你能否全部答出,都不要對自己產生任何正面或消極的評價!(面試題均來自群成員提供)

面試題預覽:

KVO實現原理?

說說你理解的埋點?

消息轉發機制原理?

說說你理解weak屬性?

假如Controller太臃腫,如何優化?

項目中網絡層如何做安全處理?

main()之前的過程有哪些?

1.KVO實現原理?

KVO在Apple中的API文檔如下:

Automatic key-value observing is implemented using a technique called isa-swizzling… When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class …

KVO基本原理:

1.KVO是基于runtime機制實現的

2.當某個類的屬性對象第一次被觀察時,系統就會在運行期動態地創建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內實現真正的通知機制

3.如果原類為Person,那么生成的派生類名為NSKVONotifying_Person

4.每個類對象中都有一個isa指針指向當前類,當一個類對象的第一次被觀察,那么系統會偷偷將isa指針指向動態生成的派生類,從而在給被監控屬性賦值時執行的是派生類的setter方法

5.鍵值觀察通知依賴于NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey:;在一個被觀察屬性發生改變之前, willChangeValueForKey:一定會被調用,這就 會記錄舊的值。而當改變發生后,didChangeValueForKey:會被調用,繼而 observeValueForKey:ofObject:change:context: 也會被調用。

KVO深入原理:

1.Apple 使用了 isa 混寫(isa-swizzling)來實現 KVO 。當觀察對象A時,KVO機制動態創建一個新的名為: NSKVONotifying_A的新類,該類繼承自對象A的本類,且KVO為NSKVONotifying_A重寫觀察屬性的setter 方法,setter 方法會負責在調用原 setter 方法之前和之后,通知所有觀察對象屬性值的更改情況。

2.NSKVONotifying_A類剖析:在這個過程,被觀察對象的 isa 指針從指向原來的A類,被KVO機制修改為指向系統新創建的子類 NSKVONotifying_A類,來實現當前類屬性值改變的監聽;

3.所以當我們從應用層面上看來,完全沒有意識到有新的類出現,這是系統“隱瞞”了對KVO的底層實現過程,讓我們誤以為還是原來的類。但是此時如果我們創建一個新的名為“NSKVONotifying_A”的類(),就會發現系統運行到注冊KVO的那段代碼時程序就崩潰,因為系統在注冊監聽的時候動態創建了名為NSKVONotifying_A的中間類,并指向這個中間類了。

4.(isa 指針的作用:每個對象都有isa 指針,指向該對象的類,它告訴 Runtime 系統這個對象的類是什么。所以對象注冊為觀察者時,isa指針指向新子類,那么這個被觀察的對象就神奇地變成新子類的對象(或實例)了。) 因而在該對象上對 setter 的調用就會調用已重寫的 setter,從而激活鍵值通知機制。

5.子類setter方法剖析:KVO的鍵值觀察通知依賴于 NSObject 的兩個方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數值的前后分別調用2個方法: 被觀察屬性發生改變之前,willChangeValueForKey:被調用,通知系統該keyPath 的屬性值即將變更;當改變發生后, didChangeValueForKey: 被調用,通知系統該 keyPath 的屬性值已經變更;之后, observeValueForKey:ofObject:change:context: 也會被調用。且重寫觀察屬性的setter 方法這種繼承方式的注入是在運行時而不是編譯時實現的。

KVO原理圖

2.說說你理解的埋點?

以下幾篇文章寫的相當不錯,可以適當借鑒下!

iOS無埋點數據SDK實踐之路

iOS無埋點數據SDK的整體設計與技術實現

iOS無埋點SDK 之 RN頁面的數據收集

3.消息轉發機制原理?

消息轉發機制基本分為三個步驟:

動態方法解析

備用接受者

完整轉發

轉發機制原理

新建一個HelloClass的類,定義兩個方法:

@interfaceHelloClass:NSObject

- (void)hello;

+ (HelloClass *)hi;@end

動態方法解析

對象在接收到未知的消息時,首先會調用所屬類的類方法+resolveInstanceMethod:(實例方法)或者+resolveClassMethod:(類方法)。在這個方法中,我們有機會為該未知消息新增一個”處理方法”“。不過使用該方法的前提是我們已經實現了該”處理方法”,只需要在運行時通過class_addMethod函數動態添加到類里面就可以了。

void functionForMethod(id self, SEL _cmd)

{

? ? NSLog(@"Hello!");

}

Class functionForClassMethod(id self, SEL _cmd)

{

? ? NSLog(@"Hi!");

? ? return [HelloClass class];

}

#pragma mark - 1、動態方法解析

+ (BOOL)resolveClassMethod:(SEL)sel

{

? ? NSLog(@"resolveClassMethod");

? ? NSString *selString = NSStringFromSelector(sel);

? ? if ([selString isEqualToString:@"hi"])

? ? {

? ? ? ? Class metaClass = objc_getMetaClass("HelloClass");

? ? ? ? class_addMethod(metaClass, @selector(hi), (IMP)functionForClassMethod, "v@:");

? ? ? ? return YES;

? ? }

? ? return [super resolveClassMethod:sel];

}

+ (BOOL)resolveInstanceMethod:(SEL)sel

{

? ? NSLog(@"resolveInstanceMethod");

? ? NSString *selString = NSStringFromSelector(sel);

? ? if ([selString isEqualToString:@"hello"])

? ? {

? ? ? ? class_addMethod(self, @selector(hello), (IMP)functionForMethod, "v@:");

? ? ? ? return YES;

? ? }

? ? return [super resolveInstanceMethod:sel];

}

備用接受者

動態方法解析無法處理消息,則會走備用接受者。這個備用接受者只能是一個新的對象,不能是self本身,否則就會出現無限循環。如果我們沒有指定相應的對象來處理aSelector,則應該調用父類的實現來返回結果。

#pragma mark - 2、備用接收者

- (id)forwardingTargetForSelector:(SEL)aSelector

{

? ? NSLog(@"forwardingTargetForSelector");

? ? NSString *selectorString = NSStringFromSelector(aSelector);

? ? // 將消息交給_helper來處理? ? if ([selectorString isEqualToString:@"hello"]) {

? ? ? ? return _helper;

? ? }

? ? return [super forwardingTargetForSelector:aSelector];

}

在本類中需要實現這個新的接受對象

@interfaceHelloClass()

{

? ? RuntimeMethodHelper *_helper;

}

@end

@implementationHelloClass- (instancetype)init

{

? ? self = [super init];

? ? if (self)

? ? {

? ? ? ? _helper = [RuntimeMethodHelper new];

? ? }

? ? return self;

}

RuntimeMethodHelper 類需要實現這個需要轉發的方法:

#import"RuntimeMethodHelper.h"

@implementationRuntimeMethodHelper- (void)hello

{

? ? NSLog(@"%@, %p", self, _cmd);

}@end

完整消息轉發

如果動態方法解析和備用接受者都沒有處理這個消息,那么就會走完整消息轉發:

#pragma mark - 3、完整消息轉發

- (void)forwardInvocation:(NSInvocation *)anInvocation

{

? ? NSLog(@"forwardInvocation");

? ? if ([RuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {

? ? ? ? [anInvocation invokeWithTarget:_helper];

? ? }

}

/*必須重新這個方法,消息轉發機制使用從這個方法中獲取的信息來創建NSInvocation對象*/

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

{

? ? NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];

? ? if (!signature)

? ? {

? ? ? ? if ([RuntimeMethodHelper instancesRespondToSelector:aSelector])

? ? ? ? {

? ? ? ? ? ? signature = [RuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];

? ? ? ? }

? ? }

? ? return signature;

}

4.說說你理解weak屬性?

weak實現原理:

Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針。weak表其實是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象的地址)數組。

1、初始化時:runtime會調用objc_initWeak函數,初始化一個新的weak指針指向對象的地址。

2、添加引用時:objc_initWeak函數會調用 objc_storeWeak() 函數, objc_storeWeak() 的作用是更新指針指向,創建對應的弱引用表。

3、釋放時,調用clearDeallocating函數。clearDeallocating函數首先根據對象地址獲取所有weak指針地址的數組,然后遍歷這個數組把其中的數據設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。

追問的問題一:

1.實現weak后,為什么對象釋放后會自動為nil?

runtime 對注冊的類, 會進行布局,對于 weak 對象會放入一個 hash 表中。 用 weak 指向的對象內存地址作為 key,當此對象的引用計數為 0 的時候會 dealloc,假如 weak 指向的對象內存地址是 a ,那么就會以 a 為鍵, 在這個 weak 表中搜索,找到所有以 a 為鍵的 weak 對象,從而設置為 nil 。

追問的問題二:

2.當weak引用指向的對象被釋放時,又是如何去處理weak指針的呢?

1、調用objc_release

2、因為對象的引用計數為0,所以執行dealloc

3、在dealloc中,調用了_objc_rootDealloc函數

4、在_objc_rootDealloc中,調用了object_dispose函數

5、調用objc_destructInstance

6、最后調用objc_clear_deallocating,詳細過程如下:

a. 從weak表中獲取廢棄對象的地址為鍵值的記錄

b. 將包含在記錄中的所有附有 weak修飾符變量的地址,賦值為? nil

c. 將weak表中該記錄刪除

d. 從引用計數表中刪除廢棄對象的地址為鍵值的記錄

5.假如Controller太臃腫,如何優化?

1.將網絡請求抽象到單獨的類中

方便在基類中處理公共邏輯;

方便在基類中處理緩存邏輯,以及其它一些公共邏輯;

方便做對象的持久化。

2.將界面的封裝抽象到專門的類中

構造專門的 UIView 的子類,來負責這些控件的拼裝。這是最徹底和優雅的方式,不過稍微麻煩一些的是,你需要把這些控件的事件回調先接管,再都一一暴露回 Controller。

3.構造 ViewModel

借鑒MVVM。具體做法就是將 ViewController 給 View 傳遞數據這個過程,抽象成構造 ViewModel 的過程。

4.專門構造存儲類

專門來處理本地數據的存取。

5.整合常量

6.項目中網絡層如何做安全處理?

1、盡量使用https

https可以過濾掉大部分的安全問題。https在證書申請,服務器配置,性能優化,客戶端配置上都需要投入精力,所以缺乏安全意識的開發人員容易跳過https,或者拖到以后遇到問題再優化。https除了性能優化麻煩一些以外其他都比想象中的簡單,如果沒精力優化性能,至少在注冊登錄模塊需要啟用https,這部分業務對性能要求比較低。

2、不要傳輸明文密碼

不知道現在還有多少app后臺是明文存儲密碼的。無論客戶端,server還是網絡傳輸都要避免明文密碼,要使用hash值。客戶端不要做任何密碼相關的存儲,hash值也不行。存儲token進行下一次的認證,而且token需要設置有效期,使用refresh

token去申請新的token。

3、Post并不比Get安全

事實上,Post和Get一樣不安全,都是明文。參數放在QueryString或者Body沒任何安全上的差別。在Http的環境下,使用Post或者Get都需要做加密和簽名處理。

4、不要使用301跳轉

301跳轉很容易被Http劫持攻擊。移動端http使用301比桌面端更危險,用戶看不到瀏覽器地址,無法察覺到被重定向到了其他地址。如果一定要使用,確保跳轉發生在https的環境下,而且https做了證書綁定校驗。

5、http請求都帶上MAC

所有客戶端發出的請求,無論是查詢還是寫操作,都帶上MAC(Message Authentication

Code)。MAC不但能保證請求沒有被篡改(Integrity),還能保證請求確實來自你的合法客戶端(Signing)。當然前提是你客戶端的key沒有被泄漏,如何保證客戶端key的安全是另一個話題。MAC值的計算可以簡單的處理為hash(request

params+key)。帶上MAC之后,服務器就可以過濾掉絕大部分的非法請求。MAC雖然帶有簽名的功能,和RSA證書的電子簽名方式卻不一樣,原因是MAC簽名和簽名驗證使用的是同一個key,而RSA是使用私鑰簽名,公鑰驗證,MAC的簽名并不具備法律效應。

6、http請求使用臨時密鑰

高延遲的網絡環境下,不經優化https的體驗確實會明顯不如http。在不具備https條件或對網絡性能要求較高且缺乏https優化經驗的場景下,http的流量也應該使用AES進行加密。AES的密鑰可以由客戶端來臨時生成,不過這個臨時的AES

key需要使用服務器的公鑰進行加密,確保只有自己的服務器才能解開這個請求的信息,當然服務器的response也需要使用同樣的AES

key進行加密。由于http的應用場景都是由客戶端發起,服務器響應,所以這種由客戶端單方生成密鑰的方式可以一定程度上便捷的保證通信安全。

7、AES使用CBC模式

不要使用ECB模式,記得設置初始化向量,每個block加密之前要和上個block的秘文進行運算。

7.main()之前的過程有哪些?

1、main之前的加載過程

1)dyld 開始將程序二進制文件初始化

2)交由ImageLoader 讀取 image,其中包含了我們的類,方法等各種符號(Class、Protocol 、Selector、 IMP)

3)由于runtime 向dyld 綁定了回調,當image加載到內存后,dyld會通知runtime進行處理

4)runtime 接手后調用map_images做解析和處理

5)接下來load_images 中調用call_load_methods方法,遍歷所有加載進來的Class,按繼承層次依次調用Class的+load和其他Category的+load方法

6)至此 所有的信息都被加載到內存中

7)最后dyld調用真正的main函數

注意:dyld會緩存上一次把信息加載內存的緩存,所以第二次比第一次啟動快一點

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評論 0 9
  • 1.weak和assign區別 修飾變量類型的區別: weak 只可以修飾對象。如果修飾基本數據類型,編譯器會報錯...
    coderjon閱讀 1,027評論 0 1
  • 各位領導、同仁: 本著發現問題、分析問題、解決問題的原則,現把黃岡項目目前出現的重點、難點提出來,以期得到各...
    子元1314閱讀 266評論 0 1
  • 關于南帆老師的拙評《關系主義文學史觀與當代文學、文化圖景的重構》選入劉小新、楊健民主編的《當代美學的文化使命與理論...
    鄭潤良閱讀 113評論 0 0
  • 當我想你的時候 我心里感到很難受 明知相思 無用處 無奈我心里 難忍相思苦 當我想你的時候 我心里感到很難受 人都...
    秋AldrichB果閱讀 258評論 2 9