Unit 3

使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

答案是:在ARC和MRC下均不需要。

以下代码摘自2011,Apple API官方文档 - Associative References:(在MRC下)

static char overviewKey;

NSArray *array =
    [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];
// For the purposes of illustration, use initWithFormat: to ensure
// the string can be deallocated
NSString *overview =
    [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];

objc_setAssociatedObject (
    array,
    &overviewKey,
    overview,
    OBJC_ASSOCIATION_RETAIN
);

[overview release];
// (1) overview valid
[array release];
// (2) overview invalid

文档中指明:当宿主对象被销毁时,其所关联的对象随后也会被销毁。

根据WWDC 2011,Session 332中发布的内存销毁时间表(由微博@iOS程序犭袁翻译)可知具体的时刻:

//于2017.8.9记
//计算引用计数的源码
- (NSUInteger)retainCount {
    return ((id)self)->rootRetainCount();
}

inline uintptr_t objc_object::rootRetainCount() {
    isa_t bits = LoadExclusive(&isa.bits);
    uintptr_t rc = 1 + bits.extra_rc;
    if (bits.has_sidetable_rc) {
        rc += sidetable_getExtraRC_nolock();
    }
    return rc;
}

/*
    从运行时源码可以知道,retainCount有三部分组成:

    - 1
    - bits.extra_rc
    - sidetable_getExtraRC_nolock

    可以清楚知道,引用计数min为1.
*/
1. 调用 -release :引用计数变为零(勘误:当对象的引用计数为1时,接收到release消息时,引用计数将不会改变,因为系统已经明确知道该对象将被销毁,改变它的引用计数已经毫无意义。(此处的关于引用计数的结果是通过调用retainCount来获取的,然而在https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571952-retaincount)这里官方说明了这个方法是不可靠的。故这里的论述并不恰当,目前任未找到合适的方法取得正确的RC -- 2017-8-9记)(于2017-7-17改)
     * 对象正在被销毁,生命周期即将结束.
     * 不能再有新的 __weak 弱引用, 否则将指向 nil.
     * 调用 [self dealloc] 
 2. 子类 调用 -dealloc
     * 继承关系中最底层的子类 在调用 -dealloc
     * 如果是 MRC 代码 则会手动释放实例变量们(iVars)
     * 继承关系中每一层的父类 都在调用 -dealloc
 3. NSObject 调 -dealloc
     * 只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法
 4. 调用 object_dispose()
     * 为 C++ 的实例变量们(iVars)调用 destructors 
     * 为 ARC 状态下的 实例变量们(iVars) 调用 -release 
     * 解除所有使用 runtime Associate方法关联的对象
     * 解除所有 __weak 引用
     * 调用 free()

亦即被关联的对象在生命周期内要比宿主对象本身释放晚很多。

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

由于解答者的篇幅过大,这里简明扼要给出解答,更多详情请猛戳文章后面所附上的文章链接。

当向一个对象发送一条消息时,如果该对象所属类没有实现该方法,运行时就会使用objc_msgForward()函数进行消息转发,即Fast forwardingNormal forwarding。在消息转发之前,这个函数指针会成为消息的IMP,进行消息转发时则执行IMP

直接使用此函数是很危险的,如果你没有为Fast forwarding、Normal forwarding做相关支持的话,程序就会崩溃。但是如果你是故意为之的话,它可以帮助我们实现一些很Cool的东西,一个典型代表就是JSPatch.(非常不安全!易被不法分子利用,现在所有涉及到这个框架的App已经全部下架。)

JSPatch以小巧的体积做到了让JS调用/替换任意OC方法,让iOS APP具备热更新的能力。(现已被禁用,具体原因可自行搜索。

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

runtime 会对注册的类进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为键( key),当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil,在这篇文章的第一条tip里对weak类型的对象释放时间表有所交代。(以观察者模式实现)

能向编译后得到的类和运行时新建的类中添加实例变量吗?

  • 不能向编译后得到的类中添加实例变量
  • 能向运行时新建的类中添加实例变量

前者区别于为对象动态绑定属性,因为动态绑定是针对于单个独立的对象,这是在运行时获得的特质,这种表现与类毫无关系,以下是一段动态绑定的代码:

- (void)setCallBackDelegate:(id<RefreshingCallBack>)callBackDelegate {
    objc_setAssociatedObject(self, ((__bridge void *)@"callBackDelegate"), callBackDelegate, OBJC_ASSOCIATION_ASSIGN);//从self就可显而易见,这个属性是对于当前的这个对象
}

下面就是关于前者不可行的原因:

编译后的类是注册在runtime中的,该类用于记录实例变量的链表objc_ivar_list结构体和实例变量的内存大小instance_size已经确定,同时runtime 会调用 class_setIvarLayoutclass_setWeakIvarLayout 来处理 strongweak 引用。在Objective-C中类的本质是结构体,所以对象在内存中的排布是确定的,它的大小是不能动态更改的。而所谓的动态绑定机制并不是改变了对象的内存结构。

下面是后者可行的原因:

在你注册新类时调用 class_addIvar 即可达成目的,但前提是在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。如果你想手动实现KVO的话,你会用到这个。

以下代码运行结果如何?

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
}

执行完 NSLog(@”1”)后,主线程进入死锁状态。

dispatch_sync()是与线程同步执行的函数,造成这段代码死锁的原因是:

viewDidLoad是在主线程中被执行的,即dispatch_get_main_queue(),执行到这个与主线程同步的函数时,它的逻辑代码块(Block),对应于代码中的NSLog(@”2”),就会被同步插入到主线程中,这个函数的返回时机是在逻辑代码块执行完毕后。因为主线程是串行队列,dispatch_sync()的执行时机在viewDidLoad执行完毕之后。而viewDidLoad的执行完毕又依赖于dispatch_sync()的返回,dispatch_sync()的执行返回又依赖于viewDidLoad的任务结束,从而导致死锁,程序将会卡死,将不再执行下去。

参考并整理自:

@微博@iOS程序犭袁:iOSInterviewQuestions
sunnyxx:招聘一个靠谱的iOS
唐巧:Objective-C对象模型及应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值