iOS 底层原理 自测(一)

iOS 底层原理 文章汇总

一、选择题

有多选,有单选

1、在LP64下,一个指针的有多少个字节

  • A: 4
  • B: 8
  • C: 16
  • D: 64

解析:1个指针8字节

2、一个实例对象的内存结构存在哪些元素

  • A:成员变量
  • B: supClass
  • C: cache_t
  • D: bit

解析: 实例对象的大小由成员变量决定。其中BCD是类的结构

3、下面 sizeof(struct3)大小等于

struct LGStruct1 {
    char b;
    int c;
    double a; -- 逢8归零
    short d;
}struct1;  -- 24

struct LGStruct2 {
    double a; 
    int b;
    char c;
    short d;
}struct2; -- 16


struct LGStruct3 {
    double a;
    int b;
    char c;
    struct LGStruct1 str1;
    short d;
    int e;
    struct LGStruct2 str2;
}struct3;
  • A: 48
  • B: 56
  • C: 64
  • D: 72

解析: 整体归零法

  • LGStruct1 看最大a,意味着前面b+c占8字节,a占8字节,c占2字节,需要对齐,即满足8的倍数 ==> 24字节

  • LGStruct2 看最大a,a占8字节,b+c+a 占8字节,对齐==> 16字节

  • LGStruct3 a占8字节,b+c占8字节,str1占24字节,d+e占8字节,str2占16自己, 对齐==>64字节

4、下列代码: re1 re2 re3 re4 re5 re6 re7 re8输出结果

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

解析:

  • +isKindOfClass:元类继承链 vs 传入类

  • -isKindOfClass:类继承链 vs 传入类

  • +isMemberOfClass:类的元类 vs 传入类

  • -isMemberOfClass:对象父类 vs 传入类

5、(x + 7) & ~7 这个算法是几字节对齐

  • A: 7
  • B: 8
  • C: 14
  • D: 16

解析: 8自己对齐(抹零后三位)
带入实际数据计算,例如(8+7)& ~7

  • 8+7 => 1111

  • ~7 => 1000

  • & => 1000

6、判断下列数据结构大小

union kc_t {
    uintptr_t bits;
    struct {
        int a;
        char b;
    };
}
  • A: 8
  • B: 12
  • C: 13
  • D: 16

解析:联合体共用内存 ,即互斥

7、元类的 isa 指向谁, 根元类的父类是谁

  • A: 自己 , 根元类
  • B: 自己 , NSObject
  • C: 根元类 , 根元类
  • D: 根元类 , NSObject

解析:经典的isa走位图


isa走位图

8、查找方法缓存的时候发现是乱序的, 为什么? 哈希冲突怎么解决的

  • A: 哈希函数原因 , 不解决
  • B: 哈希函数原因 , 再哈希
  • C: 他存他的我也布吉岛 , 再哈希
  • D: 他乱由他乱,清风过山岗 , 不解决

解析:具体实现看objc源码

9、消息的流程是

  • A: 先从缓存快速查找
  • B: 慢速递归查找 methodlist (自己的和父类的,直到父类为nil)
  • C: 动态方法决议
  • D: 消息转发流程

解析:cache快速查找 - 慢速继承链递归查找 - 动态方法决议 - 消息转发(快速转发 + 慢速转发)

10、类方法动态方法决议为什么在后面还要实现 resolveInstanceMethod

  • A: 类方法存在元类(以对象方法形式存在), 元类的父类最终是 NSObject 所以我们可以通过resolveInstanceMethod 防止 NSObject 中实现了对象方法!
  • B: 因为在oc的底层最终还是对象方法存在
  • C: 类方法存在元类以对象方法形式存在.
  • D: 咸吃萝卜,淡操心! 苹果瞎写的 不用管

解析:万物皆对象 、isa走位图

二、判断题

1、光凭我们的对象地址,无法确认对象是否存在关联对象

解析:可以通过isa判断关联对象的标识

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

解析:内存偏移

3、@interface LGPerson : NSObject{ UIButton *btn } 其中 btn 是实例变量

解析:

  • 属性 = getter + setter + 成员变量

  • 成员变量 = 没有下划线的变量 + {}中定义

  • 实例变量 = 具备实例化的变量,是一种特殊的成员变量

4、NSObject 除外 元类的父类 = 父类的元类

解析:isa走位图

5、对象的地址就是内存元素的首地址

6、类也是对象

解析:万物皆对象

三、简答题

1、怎么将上层OC代码还原成 C++代码

解析:

  • clang -rewrite-objc xxx.m -o xxx.cpp

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

2、怎么打开汇编查看流程,有什么好处 ?

解析:

  • Xcode - Debug - workflow - 勾选always show disassembly

  • 好处:了解当前函数更深层的汇编执行,以及函数的底层实现,方便跟踪内部代码,并找到代码来源

3、x/4gx 和 p/x 以及 p *$0 代表什么意思

解析:

  • x/4gx 输出一段内存地址,以8字节的形式输出8段

  • p/x 输出一个数据结构的地址

  • p *$0 *$0为指向某一个数据空间的指针,输出该数据的数据结构

4、类方法存在哪里? 为什么要这么设计?

解析:

  • 对象方法存储在类中,类方法存储在元类中

  • 好处:

    • 底层并未区分对象和类,其本质都是对象,即万物皆对象

    • 方法调用的本质是消息发送,只是消息的接受者(即方法的查找对象)有所区别

    • 设计更加基于对象,符合面向对象的特性

5、方法慢速查找过程中的二分查找流程,请用伪代码实现

解析:

  • 不断的找起始位置base、有效数据量count

  • 当前目标位置 = base + 有效数据量/2

  • 查找到对应位置之后,不断向前偏移,目的是为了找到第一个符合条件的数据

first = 0
probe
base = first
for(probe = 0;count != 0;count = count/2){
    probe = base + count/2
    if key == probe{
        while(probe > first && key == probe-1)
            probe--
        return probe
    }
    if key > value{
        base = probe+1
        count--
    }
}

6、ISA_MASK = 0x00007ffffffffff8ULL 那么这个 ISA_MASK 的算法意义是什么?

解析: 目的是为了得到isa中存储的class信息。

  • 大部分isa都是不纯的isa,即nonpointIsa,是一个64位的联合体位域数据,而存储class信息的部分只有其中的部分位,剩余的位置存储了其他信息

  • 读取class信息时,需要将其他位的信息清零,此时就需要用到掩码

  • 任何数据与isa_mask进行按位与操作,都只保留isa_mask对应位的信息。其目的就是遮盖不需要的位

7、类的结构里面为什么会有 rw 和 ro 以及 rwe ?

解析:

  • ro属于clean memory,即在编译时期确定的内存空间,只读,加载后不会再改变的内存

  • rw属于dirty memory,是运行时产生的内存,可读可写,可以向类中添加属性、方法等,即在运行时可以改变的内存

  • rwe相当于类的额外信息,因为在使用过程中,只有很少的类会真正改变其内容,所以为了避免资源浪费就有了rwe

  • 运行时如果需要动态向勒种添加方法、协议等,会创建rwe,并将ro的数据优先attach到rwe中。在读取时会优先返回rwe的数据,如果rwe中没有被初始化,则返回ro

    • 有扩展,从rwe获取

    • 没有扩展,从ro获取

  • rw中包含ro、rwe,其目的是为了让dirty memory占用更少的空间,将rw中可变的部分抽取出来作为rwe

  • clean memory越多越好,dirty memory越少越好。因为iOS 系统底层是虚拟内存机制,在内存不足时,会将一部分内容回收掉,后面使用时需要再次从磁盘中加载的。

    • clean memory是可以从磁盘中重新加载的内存,例如mach-o文件、动态库。

    • dirty memory是运行时产生的数据,是不能从磁盘中重新加载的,所以必须一直占用内存

    • 当系统物理内存紧张时会回收clean memory,如果dirty memory过大则会直接回收掉

  • 设计ro、rwe、rw的目的是为了更好更细致的区分clean memory和dirty memory

8、cache 在什么时候开始扩容 , 为什么?

解析:

  • 一般情况下:如果当前方法cache后,缓存的使用容量超过总容量的3/4,需要先进行扩容,扩容为原来的2倍,然后再插入本次的方法
  • 某些特殊预处理宏定义编译命令下,首次会存储满之后在进行扩容
  • 扩展选择3/4作为负载因子,是和hash表中使用的链表和红黑树数据结构有关,0.75是最符合泊松分布概率计算得出的数值,此时的hash表的空间和时间效率是最高的

9、objc_msgSend 为什么用汇编写 , objc_msgSend 是如何递归找到imp?

解析:

  • 使用汇编响应速度快

  • 使用了两个循环

    • 循环1:通过获取的mask与要查找的_cmd进行hash运行,获取下表,从而获取_cmd对应的bucket;然后通过向前平移查找,每次平移16位,如果找到对应的sel,则cacheHit;当平移到bucket的首地址时,如果还没有找到,则进入循环2

    • 循环2:首先获取末尾bucket地址,同样采用向前查找方式,向_cmd对应的地址进行平移查找

10、一个类的类方法没有实现为什么可以调用 NSObject 同名对象方法

解析: isa走位图 + 方法查找逻辑

  • 类的isa指向元类,在方法快速查找过程中,会根据类的isa找到元类,

  • 如果元类中没有该方法,怎会走到lookUpImpOrForward慢速查找流程中,会根据元类的继承链进行递归查找。其中元类的父类是根元类,根元类的父类指向NSObject,所以会找到NSObject的同名对象方法

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容