几道iOS面试题(一)

260 阅读4分钟
  1. 如果让你实现属性的weak,如何实现的?
  2. 如果让你来实现属性的atomic,如何实现?
  3. KVO为什么要创建一个子类来实现?
  4. 类结构体的组成,isa指针指向了什么?
  5. RunLoop有几种事件源?有几种模式?
  6. 方法列表的数据结构是什么?
  7. 分类是如何实现的?它为什么会覆盖掉原来的方法?

如果让你实现属性的weak,如何实现的?

原理:

  • 初始化时runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
  • 添加引用时objc_initWeak函数会调用objc_storeWeak() 函数, objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表。
  • 释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

如何实现

属性的weak,最关键的就是设置如何当Object->Dealloc的时候设置为nil

  1. 写 Setter 方法时,将其新值 关联一个对象(objc_setAssociatedObject)
  2. 并且实现该关联对象的一个回调方法,在回调方法中(dealloc)将新值设置为 nil。

如果让你来实现属性的atomic,如何实现?

atomic意为操作是原子的,意味着只有一个线程访问实例变量。atomic是线程安全的,至少在当前的存取器上是安全的。

最简单的方法就是,用runtime方法直接加线程锁,实现粗略的atomic

- (void)setTestObj:(id)testObj { 
@synchronized(self) { 
if(testObj != _testObj) { 
    _testObj = testObj; 
  }} 
 } 
- (id)testObj { 
    @synchronized(self) { 
    return_testObj;} 
 } 

KVO为什么要创建一个子类来实现?

对某个对象完成监听的注册后

isa指针 --> 新生成的中间类 (子类),然后子类重写所有的setter方法,并且该子类的- (Class) class和- (Class) superclass方法会被重写,返回父类(原始类)的Class,最后将当前对象的类改为这个KVO__前缀的子类

注:为什么不用category ? 因为会重写覆盖,子类不会。


类结构体的组成,isa指针指向了什么?

指向了mate-class

metaClass是什么?metaClass是Class对象的类

当你向一个对象发送消息,就在那个对象的方法列表中查找那个消息。

当你想一个类发送消息,就再那个类的metaClass中查找那个消息。

每个类都必须有一个唯一的metaClass,因为每个Class都有一个可能不一样的类方法。

每个类里面都有个isa指针,这个isa指针是指向metaClass(元类)。

image


RunLoop有几种事件源?有几种模式?

事件源

Run Loop对象处理的事件源分为两种:Input sources 和 Timer sources。

Input sources:用分发异步事件,通常是用于其他线程或程序的消息。

Timer sources:用分发同步事件,通常这些事件发生在特定时间或者重复的时间间隔上(Timer事件(Schedule或者Repeat))。

模式

NSDefaultRunLoopMode :默认状态下,不滑动,空闲状态,程序启动之后就会被切到这个mode

UITrackingRunLoopMode : 滑动的时候

UIInitializationRunLoopMode:私有的,可以追踪到的,这个app启动的时候是这个mode,第一个页面加载之后才回到第一个mode

NSRunLoopCommonModes:默认情况包括下第一个第二个,在这种情况下就是这两种情况都可以执行


方法列表的数据结构是什么?

struct objc_method{  
    SEL   method_name;  // 函数名称
    char* method_types; // 函数类型
    IMP   method_imp;   // 函数的具体实现 
}  
struct objc_method_list {  
    struct objc_method_list *obsolete; // 函数列表
    int    method_count;               // 函数中的个数
    struct objc_method method_list[1]; // 函数列表中的第一个函数地址
}  
常见runtime添加方法
OBJC_EXPORT BOOL class_addMethod(Class cls,SEL name,IMP imp,const char*types)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);  

分类是如何实现的?它为什么会覆盖掉原来的方法?

实现

Category实际上变成了一个方法列表, 被插入到类的信息内, 这样查表的时候就能找到Category内的方法。

1,将 Category 和它的主类(或元类)注册到哈希表中;

2,如果主类(或元类)已实现,那么重建它的方法列表。

此处需要知道是,它分为两种情况:Category中的实例方法、协议以及属性添加到上;而Category的类方法和协议添加到类的metaclass上的。

为什么会覆盖?

系统是在运行时将分类中对应的实例方法、类方法等插入到了原来类或元类的方法列表中,且靠前

所以,方法调用时通过isa指针去对应的类或元类的列表中查找对应的方法时先查到的是分类中的方法!查到后就直接调用不在继续查找。这即是覆盖的本质!