在项目中有某个功能需要用到多个
delegate对象,这就需要把delegate放到容器中,但又不想保存强引用导致delegate对象不能被释放。所以希望能在容器中只保存delegate对象的弱引用。搜索发现大家常用的方法应该是采用NSValue 的
valueWithNonretainedObject 和 nonretainedObject 来对弱引用进行封装。但是我测试发现,这么做虽然不会阻止对象的释放,但是对象释放后
n
onretainedObject 返回的并不是nil。这就很要命了。因为一个合适的弱引用应该有两个语义:
1、不会阻止对象释放 (这点做到了)
2、对象释放后置空 (这点并不满足)
1、使用支持弱引用的容器类,如:
[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]
2、自己实现一个弱引用的封装。
由于我只是想要一个set,没找到支持弱引用的set容器。所以我就使用第二个方法定义了WeakReferenceWrapper 来对弱引用进行封装。上代码:
==============================================
@interface
WeakReferenceWrapper
:
NSObject
+(id)
wrapNonretainedObject:(id)obj;
-(id)
init;
-(id)
initWithNonretainedObjec
t:(id)obj;
-(id)
get;
-(BOOL)
isEqual:(id)object;
-(NSUInteger)hash;
@end
@implementation
WeakReferenceWrapper
{
__weak
id
weakReference;
}
+(id)
wrapNonretainedObject:(id)obj
{
return
[[WeakReferenceWrapper
alloc]
initWithNonretainedObjec
t:obj];
}
-(id)
init
{
return
[self
initWithNonretainedObjec
t:nil];
}
-(id)
initWithNonretainedObjec
t:(id)obj
{
self
=
[super
init];
if
(self)
{
weakReference
=
obj;
}
return
self;
}
-(id)
get
{
return
weakReference;
}
-(BOOL)
isEqual:(id)object
{
if
(!object)
{
return
NO;
}
if
(![object
isKindOfClass:[self
class]])
{
return
NO;
}
WeakReferenceWrapper*
other
=
(WeakReferenceWrapper*)
object;
return
([self
get]
==
[other
get]);
}
-(NSUInteger)hash
{
if
(weakReference)
{
return
[weakReference
hash];
}
return
0;
}
@end
==============================================
测试代码如下:
@interface
Foo:
NSObject
-(id)
init;
-(void)
dealloc;
@end
@implementation
Foo
-(id)
init
{
self
=
[super
init];
NSLog(@"init");
return
self;
}
-(void)
dealloc
{
NSLog(@"dealloc");
}
@end
int
main(int
argc,
const
char
*
argv[])
{
@autoreleasepool
{
NSMutableSet*
foos
=
[[NSMutableSet
alloc]
init];
Foo*
foo1
=
[[Foo
alloc]
init];
WeakReferenceWrapper*
weakFoo1
=
[WeakReferenceWrapper
wrapNonretainedObject:foo1];
NSLog(@"%d",
[foos
containsObject:weakFoo1]);
[foos
addObject:weakFoo1];
NSLog(@"%d",
[foos
containsObject:weakFoo1]);
for
(WeakReferenceWrapper*
value
in
foos)
{
NSLog(@"%p",
[value
get]);
}
{
Foo*
foo2
=
[[Foo
alloc]
init];
[foos
addObject:[WeakReferenceWrapper
wrapNonretainedObject:foo2]];
for
(WeakReferenceWrapper*
value
in
foos)
{
NSLog(@"%p",
[value
get]);
}
}
for
(WeakReferenceWrapper*
value
in
foos)
{
NSLog(@"%p",
[value
get]);
}
}
return
0;
}
===========================================================
一定要注意的是:如果WeakReferenceWrapper 指向的对象被析构了。WeakReferenceWrapper 对象本身不会设置为nil,而是get方法返回nil。这是因为容器类中保存的是WeakReferenceWrapper 强引用,WeakReferenceWrapper 保存的是对象的弱引用。