注册

2020-iOS最新面试题解析(原理篇)

runtime怎么添加属性、方法等


  • ivar表示成员变量
  • class_addIvar
  • class_addMethod
  • class_addProperty
  • class_addProtocol
  • class_replaceProperty


是否可以把比较耗时的操作放在NSNotificationCenter中


  • 首先必须明确通知在哪个线程中发出,那么处理接受到通知的方法也在这个线程中调用
  • 如果在异步线程发的通知,那么可以执行比较耗时的操作;
  • 如果在主线程发的通知,那么就不可以执行比较耗时的操作


runtime 如何实现 weak 属性


首先要搞清楚weak属性的特点


weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。
为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;
然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)


那么runtime如何实现weak变量的自动置nil?


runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。
用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,
假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。


weak属性需要在dealloc中置nil么


  • 在ARC环境无论是强指针还是弱指针都无需在 dealloc 设置为 nil , ARC 会自动帮我们处理
  • 即便是编译器不帮我们做这些,weak也不需要在dealloc中置nil
  • 在属性所指的对象遭到摧毁时,属性值也会清空


// 模拟下weak的setter方法,大致如下
- (void)setObject:(NSObject *)object
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object = nil;
}];
}


一个Objective-C对象如何进行内存布局?(考虑有父类的情况)


  • 所有父类的成员变量和自己的成员变量都会存放在该对象所对应的存储空间中
  • 父类的方法和自己的方法都会缓存在类对象的方法缓存中,类方法是缓存在元类对象中
  • 每一个对象内部都有一个isa指针,指向他的类对象,类对象中存放着本对象的如下信息
    • 对象方法列表
    • 成员变量的列表
    • 属性列表
  • 每个 Objective-C 对象都有相同的结构,如下图所示

Objective-C 对象的结构图

ISA指针

根类(NSObject)的实例变量

倒数第二层父类的实例变量

...

父类的实例变量

类的实例变量


  • 根类对象就是NSObject,它的super class指针指向nil
  • 类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类


一个objc对象的isa的指针指向什么?有什么作用?


  • 每一个对象内部都有一个isa指针,这个指针是指向它的真实类型
  • 根据这个指针就能知道将来调用哪个类的方法


下面的代码输出什么?


@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end


  • 答案:都输出 Son
  • 这个题目主要是考察关于objc中对 self 和 super 的理解:
    • self 是类的隐藏参数,指向当前调用方法的这个类的实例。而 super 本质是一个编译器标示符,和 self 是指向的同一个消息接受者
    • 当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;
    • 而当使用 super时,则从父类的方法列表中开始找。然后调用父类的这个方法
    • 调用[self class] 时,会转化成 objc_msgSend函数
id objc_msgSend(id self, SEL op, ...)
    • 调用 [super class]时,会转化成 objc_msgSendSuper函数
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
    • 第一个参数是 objc_super 这样一个结构体,其定义如下
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};
    • 第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self
    • 第二个成员是记录当前类的父类是什么,告诉程序从父类中开始找方法,找到方法后,最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用, 此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son
    • objc Runtime开源代码对- (Class)class方法的实现


    -(Class)class {
return object_getClass(self);
}



0 个评论

要回复文章请先登录注册