人才輩出的iOS開發者,你憑什麼脫穎而出?web
與崗位要求相去甚遠,如何挑戰極限?面試
想去心怡公司,如何馬到成功?數組
那麼,你的絕招是什麼呢?瀏覽器
在這個iOS崗位供不該求的市場,對iOS開發者對要求日益增加,面試都是一項耗時耗錢的項目,而面對iOS開發者及設計師在面試時可能會遇到的問題小編進行了篩選與彙總,供廣大iOS開發者參考緩存
1.KVO實現原理?2.說說你理解的埋點?安全
3.消息轉發機制原理?性能優化
4.說說你理解weak屬性?服務器
5.假如Controller太臃腫,如何優化?網絡
6.項目中網絡層如何作安全處理?app
7.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?方法這種繼承方式的注入是在運行時而不是編譯時實現的。
2.消息轉發機制原理?
消息轉發機制基本分爲三個步驟:
一、動態方法解析
二、備用接受者
三、完整轉發
轉發機制原理
新建一個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 - 一、動態方法解析
`+ (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 - 二、備用接收者
`- (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 - 三、完整消息轉發
`- (``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指針的地址(這個地址的值是所指對象的地址)數組。一、初始化時:runtime會調用objc_initWeak函數,初始化一個新的weak指針指向對象的地址。
二、添加引用時:objc_initWeak函數會調用 objc_storeWeak() 函數, objc_storeWeak() 的做用是更新指針指向,建立對應的弱引用表。
三、釋放時,調用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指針的呢?一、調用objc_release
二、由於對象的引用計數爲0,因此執行dealloc
三、在dealloc中,調用了_objc_rootDealloc函數
四、在_objc_rootDealloc中,調用了object_dispose函數
五、調用objc_destructInstance
六、最後調用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.項目中網絡層如何作安全處理?
一、儘可能使用https
https能夠過濾掉大部分的安全問題。https在證書申請,服務器配置,性能優化,客戶端配置上都須要投入精力,因此缺少安全意識的開發人員容易跳過https,或者拖到之後遇到問題再優化。https除了性能優化麻煩一些之外其餘都比想象中的簡單,若是沒精力優化性能,至少在註冊登陸模塊須要啓用https,這部分業務對性能要求比較低。
二、不要傳輸明文密碼
不知道如今還有多少app後臺是明文存儲密碼的。不管客戶端,server仍是網絡傳輸都要避免明文密碼,要使用hash值。客戶端不要作任何密碼相關的存儲,hash值也不行。存儲token進行下一次的認證,並且token須要設置有效期,使用refresh
token去申請新的token。
三、Post並不比Get安全
事實上,Post和Get同樣不安全,都是明文。參數放在QueryString或者Body沒任何安全上的差異。在Http的環境下,使用Post或者Get都須要作加密和簽名處理。
四、不要使用301跳轉
301跳轉很容易被Http劫持攻擊。移動端http使用301比桌面端更危險,用戶看不到瀏覽器地址,沒法察覺到被重定向到了其餘地址。若是必定要使用,確保跳轉發生在https的環境下,並且https作了證書綁定校驗。
五、http請求都帶上MAC
全部客戶端發出的請求,不管是查詢仍是寫操做,都帶上MAC(Message Authentication
Code)。MAC不但能保證請求沒有被篡改(Integrity),還能保證請求確實來自你的合法客戶端(Signing)。固然前提是你客戶端的key沒有被泄漏,如何保證客戶端key的安全是另外一個話題。MAC值的計算能夠簡單的處理爲hash(request
params+key)。帶上MAC以後,服務器就能夠過濾掉絕大部分的非法請求。MAC雖然帶有簽名的功能,和RSA證書的電子簽名方式卻不同,緣由是MAC簽名和簽名驗證使用的是同一個key,而RSA是使用私鑰簽名,公鑰驗證,MAC的簽名並不具有法律效應。
六、http請求使用臨時密鑰
高延遲的網絡環境下,不經優化https的體驗確實會明顯不如http。在不具有https條件或對網絡性能要求較高且缺少https優化經驗的場景下,http的流量也應該使用AES進行加密。AES的密鑰能夠由客戶端來臨時生成,不過這個臨時的AES
key須要使用服務器的公鑰進行加密,確保只有本身的服務器才能解開這個請求的信息,固然服務器的response也須要使用一樣的AES
key進行加密。因爲http的應用場景都是由客戶端發起,服務器響應,因此這種由客戶端單方生成密鑰的方式能夠必定程度上便捷的保證通訊安全。
七、AES使用CBC模式
不要使用ECB模式,記得設置初始化向量,每一個block加密以前要和上個block的祕文進行運算。
7.main()以前的過程有哪些?
一、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會緩存上一次把信息加載內存的緩存,因此第二次比第一次啓動快一點
小編給你們推薦一個iOS底層進階和麪試內推交流羣: 923910776 羣內整理了目前各個大廠最多見的兩百多道面試題以及答案,以及各類iOS底層的學習資料!無論你是求職者仍是招聘者歡迎你的加入!