我知道unsafe_unretained
的定义。
所以我不希望有人写它的定义。
我想知道它的用法和示例,以及它如何与内存管理一起使用。
unsafe_unretained
只在 ARC(Automatic Reference Counting)中存在。它的作用类似于 MRC(Manual Reference Counting)中的 assign
。这些属性不会被保留。通常你会想要将这样的属性用于代理,因为它们不需要一个拥有者来保留它们。
weak
属性与 unsafe_unretained
类似,但是它们的工作更加智能一些。当分配给该属性的对象释放时,弱引用自动变成 nil
,以避免向该对象(内存地址)发送消息时导致崩溃。
unsafe_unretained
属性则不会做到这一点。它们将始终保留指定给它的内存地址(除非您手动更改)。在这种情况下,弱引用可以防止崩溃,但结果仍然不如预期。如果您的代码组织良好并且写得很好,则不应该出现这种情况。
那么为什么要使用 unsafe_unretained
而不是 weak
呢?弱引用仅适用于 iOS 5 及更高版本,因此如果您正在构建针对 iOS 4 的 ARC 应用程序,则需要使用 unsafe_unretained
属性。再次强调一下,向已释放的属性发送消息不是任何代码中想要发生的事情。如果您的代码组织良好,则不应该出现任何问题。
在ARC之前,为了防止保留循环,可以将委托或其他父引用属性指定为assign
。随着ARC和新编译器的引入,您现在应该使用unsafe_unretained
。
因此,在不需要对引用拥有所有权时,以及不需要或不想使用新的weak
引用类型(在引用被释放时将其设置为null)时,可以使用它。
这里有一个关于unsafe_unretained的具体用例。假设两个类相互引用,其中一个方向是强引用,另一个方向是弱引用。在第一个类的dealloc期间,来自第二个类的弱引用将已经为nil,从而防止进行适当的清理。将弱引用替换为unsafe_unretained引用将解决此问题。请参见下面的代码示例:
@class Foo;
@interface Bar: NSObject
//Replacing weak with unsafe_unretained prevents this property from becoming nil during Foo.dealloc
@property (nonatomic, weak) Foo *foo;
- (id)initWithFoo:(Foo *)foo;
@end
@interface Foo : NSObject
@property (nonatomic, strong) Bar *bar;
- (void)startObserving;
- (void)endObserving;
@end
@implementation Bar
- (id)initWithFoo:(Foo *)foo {
if ((self = [super init])) {
self.foo = foo;
//Start observing
[self.foo startObserving];
}
return self;
}
- (void)dealloc {
//Since foo is a weak property, self.foo may actually be nil at this point! See dealloc of class Foo.
[self.foo endObserving];
}
@end
@implementation Foo
- (id)init {
if ((self = [super init])) {
self.bar = [[Bar alloc] initWithFoo:self];
}
return self;
}
- (void)dealloc {
//This will trigger the deallocation of bar. However, at this point all weak references to self will return nil already!
self.bar = nil;
//endObserving is never called, because Bar.foo reference was already nil.
}
- (void)startObserving {
NSLog(@"Start observing");
}
- (void)endObserving {
NSLog(@"End observing");
}
@end
unsafe_unretained
完全不涉及内存管理。在 ARC 中,以下代码
{
__strong id obj = someObject;
obj = someOtherObject;
}
等同于
{
id obj = [someObject retain];
[obj release];
obj = [someOtherObject retain];
[obj release];
}
__strong
,除非您想强调变量是强引用。nil
。__weak id wobj = nil;
{
__strong id obj = [Object new];
wobj = obj;
// wobj points to the same object as obj
}
// wobj is nil again
在作用域内,obj
保持对象的生命周期,因此 wobjc
指向与 obj
相同的对象。但是当 obj
超出作用域时,该对象会被释放,因为这是唯一的强引用,所以它将被释放。因此,wobj
变成了 nil
。
unsafe_unretained
是指没有关于 Obj-C 内存管理的任何知识的对象指针。代码如下:
_unsafe_unretained id wobj = nil;
{
__strong id obj = [Object new];
wobj = obj;
// wobj points to the same object as obj
}
// wobj points to the memory address where an object used to be.
// This object has been deallocated! wobj is a dangling pointer!
// Trying to access the object via wobj may crash the app or cause
// other undefined behavior.
等同于
id wobj = nil;
{
id obj = [Object new];
wobj = obj;
[obj release];
}
在非ARC环境下,wobj
不会是nil
,但它曾经指向的对象已经被释放了,内存可能不再属于该进程,或者已经被回收用于存储另一个对象或任何类型的分配数据(例如使用malloc()
)。
通常情况下,您希望对不保持对象活动状态的引用使用弱引用。一个典型的问题是,如果对象A对对象B有强引用A->B
,反之亦然B->A
,那么就会出现保留循环,现在无论是A还是B都不会再次被释放,因为A使B保持活动状态,而B使A保持活动状态,即使没有其他人引用A或B,两个对象始终都将具有至少一个保留计数。要解决这个问题,其中一个必须对另一个使用弱引用。
nil
。管理该链接列表需要消耗时间,而列表本身需要消耗内存。然后,每个弱引用必须暂时变为强引用,以确保在调用其方法时不会释放对象。最后,所有这些操作都必须是线程安全的,因此即使您的进程是单线程的,它也始终涉及锁定。__weak id obj = someObject;
[obj performMethod];
obj = nil;
实际上,在幕后有复杂的代码,大致相当于
lock(memlockOf(someObject));
id obj = someObject;
addWeakRefToLinkList(someObject, &obj);
unlock(memlockOf(someObject));
lock(memlockOf(obj));
id temp = [obj retain];
unlock(memlockOf(obj));
[temp performMethod];
[temp release];
lock(memlockOf(obj));
removeWeakRefFromLinkList(obj, &obj);
obj = nil;
unlock(memlockOf(obj));
id obj = [Object new];
void * cptr = (__bridge void *)obj;
那么cptr
实际上是对该对象的一个unsafe_unretained引用。如果增加保留计数,您可以使C引用“准强引用”。
id obj = [Object new];
void * cptr = (__bridge_retained void *)obj;
这相当于
id obj = [Object new];
void * cptr = (void *)[obj retain];
在非ARC环境下,保留计数器会增加一个,因此ARC永远不会释放该对象,即使所有ARC引用都消失了,该对象仍然至少有一个保留计数器。
但现在这个对象根本不会被释放,除非你使用转换桥接来平衡它:
{
id obj = (__bridge_transfer id)cptr;
// Use obj all you want but don't touch cptr after that!
}
这相当于
{
id obj = (id)cptr;
// Use obj all you want but don't touch cptr after that!
[obj release];
}