iOS Array().addObject(ViewController())造成控制器不释放

场景

项目中有一个单例Singleton。有多个UIViewController需要向Singleton中注册观察者来接受Singleton的消息。
这种情况下,使用NSNotificationCenter的方案有些松散,因此不作考虑;
还有一种是使用delegate的方式。为了让delegate可以实现一对多的功能,可以在Singleton的里面增加一个mutableArray的变量和一个addDelegate:的方法。然后在需要接受这个Singleton通知的地方Singleton().addDelegate(self),这样可以达到目的,但是你会发现这个控制器再也不会调用dealloc

问题1

那个调用了Singleton().addDelegate(self)的控制器为什么不会dealloc()了呢?

分析1

  1. Singleton()作为单例是不会释放的;
  2. mutableArraySingleton()强引用着,因此也是不会释放的;
  3. 接受消息的控制器被添加到了mutableArray()中,就会被mutableArray()强引用,因此也不会释放;

结论1

控制器会一直被强引用,因此不会调用dealloc()

问题2

那么,如果把Singleton().addDelegate(self)中的self改成__weak呢?

__weak UIViewController *weakself = self;
Singleton().addDelegate(weakself);

分析2

先来看一段代码:

_arr = [NSMutableArray array];
Dog *dog1 = [[Dog alloc] init];
NSLog(@"-- retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)(dog1)));
__weak Dog *weakDog = dog1;
NSLog(@"-- retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)(dog1)));
NSLog(@"-- retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)(weakDog)));
[self.arr addObject:weakDog];
NSLog(@"-- retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)(dog1)));
NSLog(@"-- retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)(weakDog)));

Dog *dog2 = dog1;    

NSLog(@"dog1 = %p", dog1);
NSLog(@"weakdog1 = %p", weakDog);
NSLog(@"weakdog1 = %p", self.arr.firstObject);
NSLog(@"dog2 = %p", dog2);
NSLog(@"retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)(dog1)));

打印结果是:

retain count: 1
retain count: 1
retain count: 2
retain count: 2
retain count: 3
dog1 = 0x60400000b600
weakdog1 = 0x60400000b600
weakdog1 = 0x60400000b600
dog2 = 0x60400000b600
retain count: 3

可以验证:
1. __weak的作用是在不增加对象引用计数的前提下持有对象的引用
2. mutableArray会增加所持有指针所指对象的引用计数(不论是不是强指针)
3. 指针赋值可以使新的指针指向相同的地址(与本问题无关)

结论2

使用__weak的弱指针同样会使当前控制器被数组强引用,同样会造成当前控制器不释放

结论3

通过以上分析可以看出,想要控制器得到释放,就不能让数组持有控制器的强引用,下面有三个方案可以达到这个目的:

方法1 NSValue 不要使用这个了,最近发现会造成野指针,具体原因不明。

NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];

value.nonretainedObjectValue

方法2 NSPointerArray

参见:http://blog.csdn.net/jeffasd/article/details/60774974

方法3 Bridge

增加一个中间层

@interface DelegateBridge: NSObject <aDelegate>

@property (nonatomic, assign) id <aDelegate>delegate;

- (instancetype)initWithDelegate:(id<aDelegate>)delegate;

@end

@implementation DelegateBridge

- (instancetype)initWithDelegate:(id<aDelegate>)delegate;
{
    self = [super init];
    if (self) {
        self.delegate = delegate;
    }
    return self;
}

- (void)delegateMethod {
    if (self.delegate && [self.delegate respondsToSelector:@selector(delegateMethod:)]) {
        [self.delegate delegateMethod:aMessages];
    }
}

@end

addDelegate()的方法改为:

- (void)addDelegate:(id<aDelegate>)delegate
{
    DelegateBridge *bridge = [[DelegateBridge alloc] initWithDelegate:delegate];
    [_delegates addObject:bridge];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作...
    曾令伟阅读 4,681评论 0 10
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 6,081评论 0 12
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 6,169评论 0 7
  • 最近看到了很多事: 江歌妈妈仍在为一年前的江歌案奔走,请求惩治凶手,判他死刑 北京某区幼儿园曝出虐童事件,几名老师...
    吃橘子的冬天阅读 1,651评论 0 1
  • 当纪念日的鲜花 被实用的金钱 或是果腹的美食所替代 婚姻里的激情退去了多少 对男人来说 爱情久了就变成了亲情 那么...
    窗子1990阅读 812评论 0 1