题目出处:
作者: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指针指向的内存
20、类方法存在哪里? 为什么要这么设计? 分值10分
类方法保存在元类里面,实例方法保存到类对象里面. 这么设计的好处.就是类方法只需要保存一份.不需要保存多分.
21、方法慢速查找过程中的二分查找流程,请用伪代码实现 分值10分
- 查找cache_t
- 查找当前类的方法列表
- 查找父类的方法列表
- 如果有.就将方法插入到cache_t
- 动态解析
- 消息转发
- 都没有找到.抛出异常
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;
- ro (readonly)
- rw (readwrite)
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的面试市场! 内容有深有浅,还可以继续挖坑 (👀)
略