场景
项目中有一个单例Singleton
。有多个UIViewController
需要向Singleton中注册观察者来接受Singleton
的消息。
这种情况下,使用NSNotificationCenter
的方案有些松散,因此不作考虑;
还有一种是使用delegate
的方式。为了让delegate
可以实现一对多的功能,可以在Singleton
的里面增加一个mutableArray
的变量和一个addDelegate:
的方法。然后在需要接受这个Singleton
通知的地方Singleton().addDelegate(self)
,这样可以达到目的,但是你会发现这个控制器再也不会调用dealloc
。
问题1
那个调用了Singleton().addDelegate(self)
的控制器为什么不会dealloc()
了呢?
分析1
-
Singleton()
作为单例是不会释放的; -
mutableArray
被Singleton()
强引用着,因此也是不会释放的; - 接受消息的控制器被添加到了
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];
}