内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

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

2017-10-12 16:16 出处:清屏网 人气: 评论(0

场景

项目中有一个单例 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];
}
分享给小伙伴们:
本文标签: ArrayaddObjectViewControlliOS

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号