一 选择题(单选/多选)

1. 在LP64下,一个指针的有多少个字节
  • A: 4
  • B: 8
  • C: 16
  • D: 64

答案B
解析: 1个指针8字节

2. 一个实例对象的内存结构存在哪些元素
  • A:成员变量
  • B: supClass
  • C: cache_t
  • D: bit

答案A
解析: 实例对象的大小由成员变量决定。其中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

答案C
解析: 整体归零法

  • 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

答案C
解析:
+isKindOfClass:元类继承链 vs 传入类
-isKindOfClass:类继承链 vs 传入类
+isMemberOfClass:类的元类 vs 传入类
-isMemberOfClass:对象父类 vs 传入类

5. (x + 7) & ~7 这个算法是几字节对齐
  • A: 7
  • B: 8
  • C: 14
  • D: 16

答案B
解析: 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

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

7. 元类的 isa 指向谁, 根元类的父类是谁
  • A: 自己 , 根元类
  • B: 自己 , NSObject
  • C: 根元类 , 根元类
  • D: 根元类 , NSObject

答案B D
解析:经典的isa走位图

 
OC类与元类结构图
8. 查找方法缓存的时候发现是乱序的, 为什么? 哈希冲突怎么解决的
  • A: 哈希函数原因 , 不解决
  • B: 哈希函数原因 , 再哈希
  • C: 他存他的我也布吉岛 , 再哈希
  • D: 他乱由他乱,清风过山岗 , 不解决

答案B
解析:具体实现看objc源码

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

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

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

答案A
解析:万物皆对象 、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-debug workflow-always show disassembly 可以查看汇编;
  • 查看汇编可以从更深层了解当前函数的汇编层面的执行,为 objc 源码分析提供信 息避免方向性错误,结合 memory read 可以更清楚的看到寄存器之间是如何互相配合 处理配合的;使用汇编查看流程,可以在不确定源码出处和执行流程的情况下,跟踪内 部代码,并可以找到出处!同时,结合下符号断点的方式,能够更清晰的跟踪源码实现。
3. x/4gx 和 p/x 以及 p *$0 代表什么意思

解析:

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

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

  • p *[图片上传失败...(image-3e570f-1641723002968)]

    0 为指向某一个数据空间的指针,而该指令输出的是该数据的数据结构。

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

解析:
对象方法存储在类中哦,类方法存储在元类中;
对象方法是由类实例化出来的,类是由元类实例化出来的;
这样设计的好处有以下 3 个方面:

  • 底层不用对类方法和对象方法作区分,本质上都是对象方法,方法调用都可以理解 为消息发送,只不过消息的接受者即方法的查找对象不一致;
  • 这样设计更加的基于对象,类的一切信息都存储在元类中,对象的一切信息都存储 在类中,类是元类的实例化对象,对象是类的实例化对象;存储也更符合面向对象的特 点;
  • OC 语言设计早期借鉴了另一种 smalltalk 的语言,smalltalk 中也是这么设计的;
5. 方法慢速查找过程中的二分查找流程,请用伪代码实现

解析:

  • 二分查找的前提是数据必须有序排列;
  • 不断的找起始位置(base)和有效数据量 (count);
  • 当前目标位置=起始位置(base)+有效数据量(count)/2;
  • 查找对应位置之 后不断的向前偏移,主要作用为了找到第一个符合条件的数据,即找到最前面的 category 中满足条件的方法;
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 中存储的 class 信息,大部分 isa 都是不纯的 isa,是一个 长达 64 个 byte 位的联合体位域数据,而存储 class 信息的部分只有其中的部分 byte 位置,剩下的位置存储了其他的信息;读取的时候必须把其他无效位置的 byte 位数据给遮盖住,所以需要使用 isa_mask,任何数据与 isa_mask 进行按位与操作,都只会 保留 isamask 对应 byte 位置的数据,这个算法的意义就是遮盖住不需要的地方, 这样就能让 isa 存储更多的信息;

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

解析:

  • ro 属于 clean memory,在编译即确定的内存空间,只读,加载后不会改变内容的空间;
  • rw 属于 dirty memory,rw 是运行时结构,可读可写,可以向类中添加属性、方法等, 在运行时会改变的内存;
  • rwe 相当于类的额外信息,因为在实际使用过程中,只有很少的类会真正的改变他 们的内容,所以为避免资源的消耗就有了 rwe;
  • 运行时,如果需要动态向类中添加方法协议等,会创建 rwe,并将 ro 的数据优先 attache 到 rwe 中,在读取时会优先返回 rwe 的数据,如果 rwe 没有被初始化,则返回 ro 的数据。
    rw 中包括 ro 和 rwe,其中 rw 是 dirtymemory,ro 是 clean memory;为了让 dirty memory 占用更少的空间,把 rw 中可变的部分抽取出来为 rwe;
    clean memory 越多越好,dirty memory 越少越好,因为 iOS 系统底层虚内存机制的原 因,内存不足时会把一部分内存回收掉,后面需要再次使用时从硬盘中加载出来即 swap 机制,clean memory 是可以从硬盘中重新加载的内存,iOS 中的 macho 文件动态 库都属于此类行;dirty memory 是运行时产生的数据,这部分数据不能从硬盘中重新 加载所以必须一直占据内存,当系统物理内存紧张的时候,会回收掉 clean memory 内 存,如果 dirty memory 过大则直接会被回收掉;所以 clean memory 越多越好,dirty memory 越少越好;苹果对 rw、ro、rwe 进行这么细致的划分都是为了能更好更细致 的区别 cleanmemory 和 dirty memory;
8. cache 在什么时候开始扩容 , 为什么?

解析:

  • 一般情况下:如果当前方法 cache 之后,缓存的使用容量会超过总容量的 3/4,那么 此时就不会先插入,而是先触发扩容,扩容为原来的 2 倍,然后再插入本次的方法;
  • 某些特殊预处理宏定义编译命令下,首次会存储满之后再开始扩容;
  • 扩容时选用 3/4 作为负载因子是和 hash 表底层使用的链表以及红黑树的数据结 构有关,0.75 是最符合泊松分布概率计算得出的数值,在这个数值下哈希表的空间和 时间效率都是最高的;
9. objc_msgSend 为什么用汇编写 , objc_msgSend 是如何递归找到imp?

解析:
(1)使用汇编响应速度快;
(2)这个过程中使用了两个循环:
1.循环 1:通过前面获取的 mask,与要查找的_cmd 进行 hash 运算,获取下标,从而得到 _cmd 对应的 bucket 地址,然后进行向前平移查找,每次平移 16 个字节,如果找到对应 的 sel,则 cacheHit;当平移到 buckets 的首地址,依然没有查找到,则进入第二个循环;
2.循环 2:首先会获取末尾 bucket 的地址,同样采用向前查找的方式,向_cmd 对应的地 址进行平移查找。

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

解析:isa走位图 + 方法查找逻辑
这里涉及到 isa 和 superclass 的走位以及方法查找逻辑。
首先类的 isa 指向元类,在方法快速查找时,会根据类的 isa 找到元类,元类中没有该方 法,就会走到 lookUpImpOrForward 慢速查找流程中。在慢速查找流程中,会进行 for 递归查找,根据 superclass 查找到元类的父类,也就是根元类,而根元类的 superclass 指 向了 NSObject,所以会调用到 NSObject 的同名对象方法。

11. Swift与OC的区别,为什么说OC是一门动态性语言,其动态体现在哪里?Swift是动态语言吗?

解析:
Swift与OC区别:

  1. swift数据类型都会自动判断,只区分变量var和常量let
  2. swift 中可以定义不继承于其它类的类,称之为基类(base calss)
  3. final关键字
    使用final关键修饰的类,不能被其他类继承。
    继承final修饰的类会报错:Inheritance from a final class '…..'
  4. OC一个类由.h和.m两个文件组成,而swift只有.swift一个文件,所以整体的文件数量比OC有一定减少
  5. 书写简洁

Swift优势:

  1. Swift容易阅读,语法和文件结构简易化。
  2. Swift更易于维护,文件分离后结构更清晰。
  3. Swift更加安全,它是类型安全的语言。
  4. Swift代码更少,简洁的语法,可以省去大量冗余代码
  5. Swift速度更快,运算性能更高。

动态语言与静态语言区别:
动态语言(弱类型语言)是运行时才确定数据类型的语言,变量在使用之前无需申明类型,通常变量的值是被赋值的那个值的类型。
静态语言(强类型语言)是编译时变量的数据类型就可以确定的语言,大多数静态语言要求在使用变量之前必须声明数据类型。

OC是动态语言,其动态特性表现为了三个方面:动态类型、动态绑定、动态加载

  • 动态类型:即运行时再决定对象的类型。简单说就是id类型,任何对象都可以被id指针所指,只有在运行时才能决定是什么类型。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在编译的时候就能被识别出来。所以若程序发生了类型不对应,编译器就会发出警告。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时与运行时
  • 动态绑定:基于动态类型在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。比如我们一般向一个NSObject对象发送-respondsToSelector:或者 -instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类 的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的方 法,即类的实现是可以动态绑定的,isKindOfClass也是一样的道理。
  • 动态加载:所谓动态加载就是我们做开发的时候icon图片的时候在Retina设备上要多添加一个张@2x的图片,当设备更换的时候,图片也会自动的替换。
 
 

吐血分享一套iOS底层面试题,真心想帮你!!!的更多相关文章

  1. 分享一套高级Java笔试题(实拍高清图)

    分享一套高级Java笔试题 微信群里群友分享的 刚好他在笔试 有些问题不会发到群里求助 如果你最近正好在面试 需要参考需要提升 这套试题或许对你有用 下面是部分分享原图 下面是微信群中群友的热议 非常 ...

  2. 《招聘一个靠谱的iOS》面试题参考答案(下)

    相关文章: <招聘一个靠谱的iOS>面试题参考答案(上) 说明:面试题来源是微博@我就叫Sunny怎么了的这篇博文:<招聘一个靠谱的 iOS>,其中共55题,除第一题为纠错题外 ...

  3. IOS学习笔记48--一些常见的IOS知识点+面试题

      IOS学习笔记48--一些常见的IOS知识点+面试题   1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...

  4. 李洪强iOS经典面试题153- 补充

    李洪强iOS经典面试题153- 补充   补充 有空就来解决几个问题,已经懒癌晚期没救了... UML 统一建模语言(UML,UnifiedModelingLanguage)是面向对象软件的标准化建模 ...

  5. 李洪强iOS经典面试题144-数据存储

    李洪强iOS经典面试题144-数据存储   数据存储 sqlite中插入特殊字符的方法和接收到处理方法. 除'其他的都是在特殊字符前面加"/",而 ' -> '' .方法:k ...

  6. 李洪强iOS经典面试题141-报错警告调试

    李洪强iOS经典面试题141-报错警告调试   报错警告调试 你在实际开发中,有哪些手机架构与性能调试经验 刚接手公司的旧项目时,模块特别多,而且几乎所有的代码都写在控制器里面,比如UI控件代码.网络 ...

  7. iOS经典面试题(转)

    iOS经典面试题   前言 写这篇文章的目的是因为前两天同学想应聘iOS开发,从网上找了iOS面试题和答案让我帮忙看看.我扫了一眼,倒吸了一口冷气,仔细一看,气的发抖.整篇题目30多个没有一个答案是对 ...

  8. 李洪强iOS经典面试题下

    李洪强iOS经典面试题下 21. 下面的代码输出什么? @implementation Son : Father - (id)init { self = [super init]; if (self) ...

  9. 李洪强iOS经典面试题上

    李洪强iOS经典面试题上     1. 风格纠错题 修改完的代码: 修改方法有很多种,现给出一种做示例: // .h文件 // http://weibo.com/luohanchenyilong/ / ...

  10. 分享几套古典复古式的UI设计

    古典复古的UI界面设计能够非常好的点缀你的网页,特别是如果你需要展示你的产品或者内容的深度内涵或者体验你的内容或者产品的经久不衰的气质的 话,古典或者复古式的界面UI绝对是你首选,在这里我们收集了10 ...

随机推荐

  1. [转帖]自动化回归测试工具 —— AREX 上手实践

    https://my.oschina.net/arextest/blog/8589156   AREX 是一款开源的自动化测试工具平台,基于 Java Agent 技术与比对技术,通过流量录制回放能力 ...

  2. [转帖]Native Memory Tracking 详解(2):追踪区域分析(一)

    https://www.modb.pro/db/529363 上篇文章 Native Memory Tracking 详解(1):基础介绍 中,分享了如何使用NMT,以及NMT内存 & OS内 ...

  3. 如何写RN样式 如何写RN组件 如何满屏 如何使用变量

    app.js 文本水平居中了哈 控制文本的大小 字体颜色等 只有在文本元素上去控制哈 import React from 'react'; import {View, Text, StyleSheet ...

  4. dump分析器winbdg

    工具: winbdg WinDBG不是专门用于调试.Net程序的工具,它更偏向于底层,可用于内核和驱动调试.进行普通的.Net程序调试还是使用微软专为.Net开发的调试工具MDBG更方便一些.但是Wi ...

  5. ABP无法使用异步操作,但要调用异步方法

    使用 AsyncHelper.RunSync(() => _studentRepository.FirstOrDefaultAsync(x => x.Code == studentCode ...

  6. 小白学k8s(10)-k8s中ConfigMap理解

    理解ConfigMap 什么是ConfigMap ConfigMap的创建 使用key-value 字符串创建 从env文件创建 从目录创建 通过Yaml/Json创建 ConfigMap使用 用作环 ...

  7. 通过docker-compose搭建mongo的replica set高可用

    通过docker-compose搭建mongo的replica set高可用 前言 备份数据 备份数据到本地 数据恢复 集群搭建 生成keyFile 创建yml文件 初始化副本集 增加副本集 将节点初 ...

  8. 机器学习算法(三):基于horse-colic数据的KNN近邻(k-nearest neighbors)预测分类

    机器学习算法(三):基于horse-colic数据的KNN近邻(k-nearest neighbors)预测分类 项目链接参考:https://www.heywhale.com/home/column ...

  9. Flask Paginate实现表格分页

    flask_paginate 是 Flask 框架的一个分页扩展,用于处理分页相关的功能.它可以帮助你在 Flask Web 应用程序中实现分页功能,让用户可以浏览大量数据的不同部分.本篇博文重点讲述 ...

  10. 4.0 Python 变量与作用域

    在python中,变量的作用域决定了变量在哪些位置可以被访问.一个程序中的变量并不是所有的地方都可以访问的,其访问权限决定于变量的赋值位置.python中有两种最基本的变量作用域:局部作用域和全局作用 ...