iOS底層面試題(上篇)

7月,iOS求職跳槽的相對較少,能在這個時間段求職的,不是被迫,就是對本身的技術很自信;
針對7月,特別總結了一份iOS常見大廠面試題(上);git

iOS面試題分爲 上、中、下三部分,方便你們觀看;github

請先本身答一答面試

話很少說;直接上題
本文收錄:公衆號【iOS進階寶典《iOS底層面試題(上篇)》】安全

1:談談你對KVC的理解

KVC能夠經過key直接訪問對象的屬性,或者給對象的屬性賦值,這樣能夠在運行時動態的訪問或修改對象的屬性數據結構

2:iOS項目中引用多個第三方庫引起衝突的解決方法

可能有不少小夥伴還不太清楚,動靜態庫的開發,這裏推薦一篇博客:iOS-製做.a靜態庫SDK和使用.a靜態庫多線程

若是咱們存在三方庫衝突就會保存:duplicate symbol _OBJC_IVAR_$_xxxx in:併發

目前見效最快的就是把**.framework**選中,**taggert Membership**的對勾取消掉,就編譯沒有問題了,可是後續的其餘問題可能還會出現異步

我想說的是像這種開源的使用率很高的源代碼本不該該包含在lib庫中,就算是你要包含那也要改個名字是吧。不過沒辦法如今人家既然包含,咱們就只有想辦法分離了async

  • mkdir armv7:建立臨時文件夾ide

  • lipo libALMovie.a -thin armv7 -output armv7/armv7.a:取出armv7平臺的包

  • ar -t armv7/armv7.a:查看庫中所包含的文件列表

  • cd armv7 && ar xv armv7.a:解壓出object file(即.o後綴文件)

  • rm ALButton.o:找到衝突的包,刪除掉(此步能夠屢次操做)

  • cd … && ar rcs armv7.a armv7/*.o:從新打包object file

  • 多平臺的SDK的話,須要屢次操做第4步。
    操做完成後,合併多個平臺的文件爲一個.a文件:
    lipo -create armv7.a arm64.a -output new.a

  • 將修改好的文件, 拖拽到原文件夾下,替換原文件便可。

3:GCD實現多讀單寫

好比在內存中維護一份數據,有多處地方可能會同時操做這塊數據,怎麼能保證數據安全?
這道題目總結獲得要知足如下三點:

  • 1.讀寫互斥
  • 2.寫寫互斥
  • 3.讀讀併發
@implementation KCPerson
- (instancetype)init
{
    if (self = [super init]) {
       _concurrentQueue = dispatch_queue_create("com.kc_person.syncQueue", DISPATCH_QUEUE_CONCURRENT);
       _dic = [NSMutableDictionary dictionary];
    }
    return self;
}
- (void)kc_setSafeObject:(id)object forKey:(NSString *)key{
    key = [key copy];
    dispatch_barrier_async(_concurrentQueue, ^{
       [_dic setObject:object key:key];
    });
}
- (id)kc_safeObjectForKey::(NSString *)key{
    __block NSString *temp;
    dispatch_sync(_concurrentQueue, ^{
        temp =[_dic objectForKey:key];
    });
    return temp;
}
@end
  • 首先咱們要維繫一個GCD 隊列,最好不用全局隊列,畢竟你們都知道全局隊列遇到柵欄函數是有坑點的,這裏就不分析了!

  • 由於考慮性能 死鎖 堵塞的因素不考慮串行隊列,用的是自定義的併發隊列!
    _concurrentQueue = dispatch_queue_create("com.kc_person.syncQueue", DISPATCH_QUEUE_CONCURRENT);

  • 首先咱們來看看讀操做:kc_safeObjectForKey咱們考慮到多線程影響是不能用異步函數的!說明:

  • 線程2 獲取:
    name 線程3 獲取 age

  • 若是由於異步併發,致使混亂 原本讀的是name 結果讀到了age

  • 咱們容許多個任務同時進去! 可是讀操做須要同步返回,因此咱們選擇:同步函數 (讀讀併發)

  • 咱們再來看看寫操做,在寫操做的時候對key進行了copy, 關於此處的解釋,插入一段來自參考文獻的引用:

函數調用者能夠自由傳遞一個NSMutableStringkey,而且可以在函數返回後修改它。所以咱們必須對傳入的字符串使用copy操做以確保函數可以正確地工做。若是傳入的字符串不是可變的(也就是正常的NSString類型),調用copy基本上是個空操做。

  • 這裏咱們選擇dispatch_barrier_async, 爲何是柵欄函數而不是異步函數或者同步函數,下面分析:

  • 柵欄函數任務:以前全部的任務執行完畢,而且在它後面的任務開始以前,期間不會有其餘的任務執行,這樣比較好的促使 寫操做一個接一個寫 (寫寫互斥),不會亂!

  • 爲何不是異步函數?應該很容易分析,畢竟會產生混亂!

  • 爲何不用同步函數?若是讀寫都操做了,那麼用同步函數,就有可能存在:我寫須要等待讀操做回來才能執行,顯然這裏是不合理!

4:講一下atomic的實現機制;爲何不能保證絕對的線程安全(最好能夠結合場景來講)?

A: atomic的實現機制

  • atomicproperty的修飾詞之一,表示是原子性的,使用方式爲@property(atomic)int age;此時編譯器會自動生成 getter/setter 方法,最終會調用objc_getPropertyobjc_setProperty方法來進行存取屬性。

  • 若此時屬性用atomic修飾的話,在這兩個方法內部使用os_unfair_lock 來進行加鎖,來保證讀寫的原子性。鎖都在PropertyLocks 中保存着(在iOS平臺會初始化8個,mac平臺64個),在用以前,會把鎖都初始化好,在須要用到時,用對象的地址加上成員變量的偏移量爲key,去PropertyLocks中去取。所以存取時用的是同一個鎖,因此atomic能保證屬性的存取時是線程安全的。

  • 注:因爲鎖是有限的,不用對象,不一樣屬性的讀取用的也多是同一個鎖

B: atomic爲何不能保證絕對的線程安全?

  • atomicgetter/setter方法中加鎖,僅保證了存取時的線程安全,假設咱們的屬性是@property(atomic)NSMutableArray *array;可變的容器時,沒法保證對容器的修改是線程安全的.
  • 在編譯器自動生產的getter/setter方法,最終會調用objc_getPropertyobjc_setProperty方法存取屬性,在此方法內部保證了讀寫時的線程安全的,當咱們重寫getter/setter方法時,就只能依靠本身在getter/setter中保證線程安全

5. Autoreleasepool所使用的數據結構是什麼?AutoreleasePoolPage結構體瞭解麼?

  • Autoreleasepool是由多個AutoreleasePoolPage以雙向鏈表的形式鏈接起來的.

  • Autoreleasepool的基本原理:在每一個自動釋放池建立的時候,會在當前的AutoreleasePoolPage中設置一個標記位,在此期間,當有對象調用autorelsease時,會把對象添加 AutoreleasePoolPage

  • 若當前頁添加滿了,會初始化一個新頁,而後用雙向量表連接起來,並把新初始化的這一頁設置爲hotPage,當自動釋放池pop時,從最下面依次往上pop,調用每一個對象的release方法,直到遇到標誌位。

AutoreleasePoolPage結構以下

class AutoreleasePoolPage {
    magic_t const magic;
    id *next;//下一個存放autorelease對象的地址
    pthread_t const thread; //AutoreleasePoolPage 所在的線程
    AutoreleasePoolPage * const parent;//父節點
    AutoreleasePoolPage *child;//子節點
    uint32_t const depth;//深度,也能夠理解爲當前page在鏈表中的位置
    uint32_t hiwat;
}
文末推薦:iOS熱門文集

① Swift

② iOS底層技術

③ iOS逆向防禦

④ iOS面試合集

喜歡的小夥伴記得點贊喔~

收藏等於白嫖,點贊纔是真情ღ( ´・ᴗ・` )ღ

相關文章
相關標籤/搜索