一套iOS底層試卷-答案

題目出處:
作者:Cooci
鏈接:https://juejin.cn/post/6983175020340051976
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

正文

一、選擇題(每題5分) ?? 有單選有多選哦??

在LP64下,一個指針的有多少個字節 分值5分

A: 4

B: 8

C: 16

D: 64


B

一個實例對象的內存結構存在哪些元素 分值5分

A:成員變量

B: supClass

C: cache_t

D: bit


A .實例對象的內存結構: isa + 成員變量.

其他的bcd.是類對象里面的數據

下面sizeof(struct3) 大小等于 分值5分

struct LGStruct1 {
    char b;                 1
    int c;                  4
    double a;               8
    short d;                2
}struct1;                   //15  .內存對齊 16

struct LGStruct2 {
    double a;               8
    int b;                  4
    char c;                 1
    short d;                2
}struct2;                   //15   .內存對齊 16


struct LGStruct3 {
    double a;               8
    int b;                  4
    char c;                 1
    struct LGStruct1 str1;  16
    short d;                2
    int e;                  4
    struct LGStruct2 str2;  16
}struct3;                   //51    內存對齊.內部最大的倍數.16的倍數    

復制代碼

A: 48

B: 56

C: 64

D: 72


C 內存對齊.內部最大的倍數.16的倍數

下列代碼: re1 re2 re3 re4 re5 re6 re7 re8輸出結果 分值5分

BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];   
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];  
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];  
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];    
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];    
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];     
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];   
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

復制代碼

A: 1011 1111

B: 1100 1011

C: 1000 1111

D: 1101 1111


C
1 , 類方法. 會去元類進行比較.是否是/是否是子類
0 , 類方法. 會去元類進行比較.是否是當前類
0 ,

(x + 7) & ~7 這個算法是幾字節對齊 分值5分

A: 7

B: 8

C: 14

D: 16


B 也可以寫成 (x + 7) >> 3 << 3
0111
1000

判斷下列數據結構大小 分值5分

union kc_t {
    uintptr_t bits;
    struct {
        int a;              4
        char b;             1
    };
}

復制代碼

A: 8

B: 12

C: 13

D: 16


A union 是聯合體.共同體.公用一個內存.

元類的 isa 指向誰, 根元類的父類是誰 分值5分

A: 自己 , 根元類

B: 自己 , NSObject

C: 根元類 , 根元類

D: 根元類 , NSObject


BD 。

  • 根元類的isa指向自己,
  • 非根元類的isa 指向 根元類 .
  • 根元類的父類指向NSObject.

查找方法緩存的時候發現是亂序的, 為什么? 哈希沖突怎么解決的 分值5分

A: 哈希函數原因 , 不解決

B: 哈希函數原因 , 再哈希

C: 他存他的我也布吉島 , 再哈希

D: 他亂由他亂,清風過山崗 , 不解決


B

objc_class 類對象

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    ....

cache_t 方法緩存表 . hash沖突的時候是發生在插入的方法里面的.主要關注插入的方法

struct cache_t {
...
public:
    void insert(SEL sel, IMP imp, id receiver);
    void copyCacheNolock(objc_imp_cache_entry *buffer, int len);
    void destroy();
    void eraseNolock(const char *func);
void cache_t::insert(SEL sel, IMP imp, id receiver)
{
...
    bucket_t *b = buckets();
    mask_t m = capacity - 1;  // 容量
    mask_t begin = cache_hash(sel, m); //方法名 和當前容量
    mask_t i = begin;

    // Scan for the first unused slot and insert there.
    // There is guaranteed to be an empty slot.
    do {
        if (fastpath(b[i].sel() == 0)) {
            incrementOccupied();
            b[i].set<Atomic, Encoded>(b, sel, imp, cls());
            return;
        }
        if (b[i].sel() == sel) {
            // The entry was added to the cache by some other thread
            // before we grabbed the cacheUpdateLock.
            return;
        }
    } while (fastpath((i = cache_next(i, m)) != begin));

    bad_cache(receiver, (SEL)sel);
#endif // !DEBUG_TASK_THREADS
}

unsigned cache_t::capacity() const
{
    return mask() ? mask()+1 : 0; 
}
static inline mask_t cache_hash(SEL sel, mask_t mask) 
{
    uintptr_t value = (uintptr_t)sel;
#if CONFIG_USE_PREOPT_CACHES
    value ^= value >> 7;
#endif
    return (mask_t)(value & mask);
}


#if CACHE_END_MARKER
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return (i+1) & mask;
}
#elif __arm64__
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return i ? i-1 : mask;
}
#else
#error unexpected configuration
#endif

消息的流程是 分值5分

A: 先從緩存快速查找

B: 慢速遞歸查找methodlist (自己的和父類的,直到父類為nil)

C: 動態方法決議

D: 消息轉發流程


ABCD

類方法動態方法決議為什么在后面還要實現 resolveInstanceMethod 分值5分

A: 類方法存在元類(以對象方法形式存在), 元類的父類最終是 NSObject 所以我們可以通過resolveInstanceMethod 防止 NSObject 中實現了對象方法!

B: 因為在oc的底層最終還是對象方法存在

C: 類方法存在元類以對象方法形式存在.

D: 咸吃蘿卜,淡操心! 蘋果瞎寫的 不用管


A

二、判斷題 (每題5分)

光憑我們的對象地址,無法確認對象是否存在關聯對象 分值5分



錯. 在64位的系統下.對象地址的其中一位是用來判斷是否有關聯對象.在dealloc 的時候.會通過這位是否包含關聯對象.來進行關聯對象的釋放

int c[4] = {1,2,3,4}; int *d = c; c[2] = *(d+2) 分值5分



對吧.

@interface LGPerson : NSObject{ UIButton *btn } 其中 btn 是實例變量 分值5分



NSObject 除外 元類的父類 = 父類的元類 分值5分



對象的地址就是內存元素的首地址 分值5分



類也是對象 分值5分



三、簡單題 (每題 10分 合計 100分)

請把它當成一場面試,認真對待 希望大家耐心 切忌浮躁 (和諧學習 不急不躁)

17、怎么將上層OC代碼還原成 C++代碼 分值10分

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp

18、怎么打開匯編查看流程,有什么好處 ? 分值10分

19、x/4gx 和 p/x 以及 p *$0 代表什么意思 分值10分

x/4gx 查看內存.分4塊每塊按8進制的顯示

p/x 輸出數據結構的首地址

p *0 打印0指針指向的內存

20、類方法存在哪里? 為什么要這么設計? 分值10分

類方法保存在元類里面,實例方法保存到類對象里面. 這么設計的好處.就是類方法只需要保存一份.不需要保存多分.

21、方法慢速查找過程中的二分查找流程,請用偽代碼實現 分值10分

  1. 查找cache_t
  2. 查找當前類的方法列表
  3. 查找父類的方法列表
  4. 如果有.就將方法插入到cache_t
  5. 動態解析
  6. 消息轉發
  7. 都沒有找到.拋出異常

22、ISA_MASK = 0x00007ffffffffff8ULL 那么這個 ISA_MASK 的算法意義是什么? 分值10分

主要就是內存優化.通過對象指針進行isa_mask位運算.獲取到對象的地址

23、類的結構里面為什么會有 rw 和 ro 以及 rwe ? 分值10分

類的結構主要是

  • isa.
  • superclass.
  • bits
    • rw (readwrite)
      • ro (readonly)
        • ivars
        • methods_list
        • protocol_list
        • property_list
        • ...
      • methods-array
      • protocol-array
      • property-array
      • ...
      • rwe (readwriteext,擴展)
        • class_ro_t_authed_ptr<const class_ro_t> ro;
        • method_array_t methods;
        • property_array_t properties;
        • protocol_array_t protocols;
        • char *demangledName;
        • uint32_t version;

24、cache 在什么時候開始擴容 , 為什么? 分值10分

    mask_t newOccupied = occupied() + 1;
    unsigned oldCapacity = capacity(), capacity = oldCapacity;
    if (slowpath(isConstantEmptyCache())) {
        // Cache is read-only. Replace it.
        if (!capacity) capacity = INIT_CACHE_SIZE;
        reallocate(oldCapacity, capacity, /* freeOld */false);
    }
    else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {
        // Cache is less than 3/4 or 7/8 full. Use it as-is.
    }
#if CACHE_ALLOW_FULL_UTILIZATION
    else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {
        // Allow 100% cache utilization for small buckets. Use it as-is.
    }
#endif
    else {
        capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
        if (capacity > MAX_CACHE_SIZE) {
            capacity = MAX_CACHE_SIZE;
        }
        reallocate(oldCapacity, capacity, true);
    }

25、objc_msgSend 為什么用匯編寫 , objc_msgSend 是如何遞歸找到imp? 分值10分

不用匯編.還可以用啥??.
參考方法查找的步驟

26、一個類的類方法沒有實現為什么可以調用 NSObject 同名對象方法 分值10分

類方法保存在元類中.根元類的父類的superclass 指向的是nsobject
sel方法名傳遞的時候是沒有傳遞+/- . 通過objc_msgSend 調用的方法接受者 和sel方法.

四、拓展滿分題 (20分

提交一篇大師班學習期間,你寫的最好的一篇博客 分值20分

五、總結

考試的題目偏向底層, 也比較貼合現在iOS的面試市場! 內容有深有淺,還可以繼續挖坑 (??)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容