iOS几道runtime面试题目

Date
Created
Feb 13, 2023 04:38 AM
Descrption
适用于中高级iOS工程师
Tags
iOS
面试题
runtime
notion image

1.在OC中初始化对象时,[[class_name alloc] init][class_name new]这两种方式有什么区别?

源码:
alloc的实现与new实现的对比:
+(id)alloc{ return _objc_rootAlloc(self); } id_objc_rootAlloc(Class cls){ return callAlloc(cls,false.true); } +(id)new{ return [callAlloc(self,false) init]; }
init方法的实现:
-(id)init{ return _objc_rootInit(self); } id _obj_rootInit(id obj){ return obj; }
事实上alloc+init=new,在内部实现中,参数zone已经被忽略掉了,最终都是调用class_createInstance。即alloc+init和new两种方式没有本质区别,但是如果想要initWithXXX这种形式初始化对象,那么使用alloc+init方式。一般new只是在开发人员在不熟悉OC的情况下使用的一个辅助方法。

2.Runtime如何通过selector找到对应的IMP地址?(需要分别考虑类方法和实例方法)

三种情况:
  • objc_msgSend
  • methodForSelector
  • method_getImplementation

1.objc_msgSend:

 
notion image
 

2.methodForSelector:

3.method_getImplementation

notion image
 
  • selecotr实质上是一个方法名name,遍历方法列表可查询对应的method,method中包含方法名name以及方法实现imp
  • 实例的方法在其对应的类方法列表中查询,通过isa可获取其类
  • 类的方法在其对应的元类方法列表中查询,通过isa或者getMeta

3.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

答案:不可以向编译后的类中增加实例变量,可以向运行时创建的类中添加实例比纳凉。
  • 类编译时只读结构体class_ro_t就会被确定,运行时不可修改
  • ro结构体中的ivar_list_t也是不可修改的,并且instanceSize决定了创建对象时需要的空间大小
  • objc_allocateClassPairobjc_registerClassPair之间调用class_addIvar

4.给类添加一个属性后,在类结构体里那些元素会发生变化?

首先对于@property()这种方法,以下是发生变化的地方:

  • class_ro_t->isntanceSize:实例大小
  • class_ro_t->ivar_list_t:变量列表
  • class_rw_t->method_list_t:方法列表
  • class_rw_t->property_list_t:属性列表

然而对于class_addProperty这种方法,以下是发生变化的地方:

  • class_rw_t->property_list_t:属性列表

5._objc_msgForward函数是做什么的?直接调用它将会发生什么?

 
  • _objc_msgForward是一个函数地址即IMP,用于消息转发
  • 当一个对象未执行未实现的方法时,会调用此方法
  • resolveClassMethodresolveInstanceMethod不属于消息转发流程,是属于消息查找过程中的,如果IMP就要开始动态解析,动态解析不成功后会调用消息转发。
  • 直接调用_objc_msgForward 会崩溃的,因为系统会默认你没有找到这个方法的IMP,直接进行消息转发流程,但是你后面的方法都没有实现,所以就会crash

6.使用Runtime Associate方法关联的对象,需要在主对象delloc的时候释放么?

对象的销毁步骤:

  • 主对象引用计数为0时,调用_release
  • 沿继承链调用父类的dealloc
  • 直到调用NSObjectdealloc
  • 相关函数:dealloc->_objc_rootDealloc->rootDealloc->object_despose->objc_destructInstance

对象销毁的内容:

  • 释放实例变量:object_cxxDestruct
  • 移除关联属性:_object_remove_assocations
  • 将弱引用置为nil:clearDeallocating
  • 属性关联的对象不需要在主对象dealloc中释放
  • 它们会在被NSObject-dealloc调用的object_dispose()方法中释放
  • 释放时,如果是强引用retain,则release,不是retain的话,不会管
    • notion image
 

7._runtime如何实现weak属性?那么runtime如何实现weak变量的自动置nil?

runtime源码文件中的:objc-weak.h,该文件里面有很多弱引用的相关方法:
  • 添加弱引用:weak_register_no_lock
  • 移除一个弱引用:weak_unregister_no_lock
  • 指定对象是否存在弱引用:weak_is_registered_no_lock
  • 清楚指定对象所有弱引用:weak_clear_no_lock
 
notion image
 
weak的实现:
  • 执行__weak ClassA *objB = objA
  • 会调用NSObjectobjc_initWeak方法
weak自动nil的实现:
  • 首先执行clearDeallocation方法
  • 再次调用objc-weak.h文件中的weak_clear_no_lock方法
所以
  • 当一个对象有弱引用时,会封装成一个结构体,将其放入一个数组中,并根据该对象的地址作为标识
  • 当该对象的引用计数为0时,会以其地址为标识遍历搜索数组,把指向该对象所有指针都置为nil

8.以下代码会输出什么?

@implementation Son : Father -(id)init{ self = [super init]; if(self){ NSLog(@"%@",NSStringFromClass([self class])); NSLog(@"%@",NSStringFromClass([super class])); } return self; } @end
答案:都输出son
notion image
 
NSObject中class实现
-(Class)class{ return object_getClass(self); } Class object_getClass(id obj){ if(obj) return obj->getIsa(); else return Nil; }
[super class]方法的内部实现
struct objc_super objcSuper = { self, class_getSuperClass([self class]), }; objc_msgSendSuper(&objcSuper,@selector(class));
总结:
  • Father和Son都没有实现Class方法时,二者唯一区别就是[super class]比[self class]快了一步

9.下面代码会输出什么?

BOOL res1=[(id)[NSObject class] isKindOfClass:[NSObject class]]; BOOL res2=[(id)[NSObject class] isMemberOfClass:[NSObject class]]; BOOL res3=[(id)[Sark class] isKindOfClass:[Sark class]]; BOOL res4=[(id)[Sark class] isMemberOfClass:[Sark class]];
答案:1、0、0、0
isKindOfClass:和isMemberOfClass:源码实现
+(BOOL)iskindOfClass:(Class)cls{ for(Class tcls=object_getClass((id)self);tcls;tcls=tcls->superclass){ if(tcls==cls) return YES; } return NO; } +(BOOL)isMemberOfClass:(Class)cls{ return object_getClass((id)self) == cls; }
总结:
  • isKindOfClass:判断该类对象所属的类,其继承链是否包含传入的class
  • isMemberOfClass:判断该类对象所属的类是否等于传入的class

10.下面代码会?Compile Error/Runtime Crash/NSLog…?

@interface NSObejct (Sark) +(void)foo; @end @implementation NSObject(Sark) -(void)foo{ NSLog(@"IMP: -[NSObject(Sark) foo]"); } @end //调用 [NSObject foo]; [[NSObject new] performSelector: @selector(foo)];