我有一个循环引用。如何在 Objective-C 中创建弱引用?
-
21-08-2019 - |
题
我正在开发 iPhone 应用程序。我有一个类对象 Row
需要释放该类的许多对象 Block
. 。每一个 Block
当前有一个属性保留类的实例变量 Row
.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,retain) Row *yCoord;
@end
每一个 Row
包含一个 NSMutableArray
这些块。
@interface Row : NSObject {
NSMutableArray *blocks;
}
-(void)addBlock:(Block*)aBlock;
@end
@implementation Row
-(void)addBlock:(Block*)aBlock {
[blocks addObject:aBlock];
aBlock.yCoord = self;
}
@end
我知道这是一个循环引用。苹果的文档指出,为了释放带有循环引用的对象,我需要一个弱引用而不是强引用(保留属性),但它没有跟进并解释我到底是如何做到这一点的。我计划同时释放和释放行中的所有块以及行本身。如何在每个块中设置对其“父”行的弱引用?
解决方案
编辑:由于提问者澄清他没有使用的垃圾收集(iPhone目前不支持的话),我的建议是必须避免的周期只有一个对象保留其他的,就像你会做了委托。当使用属性,使用“分配”,而不是“保留”来实现这一目标。例如:
@property (nonatomic,assign) Row *yCoord;
我的答案回答的其余部分涉及在Objective-C 2.0和GC的术语为“弱引用”。
当你与垃圾收集(10.5+)工作,弱引用是由前缀变量声明与的 __weak
强>创建。当你分配给该变量时,GC(如果启用)跟踪基准的,如果要引用的对象的所有强引用消失会自动归零出来给你。 (如果未启用GC,所述__weak
属性被忽略。)
因此,你可以安全地修改上面的回答与垃圾收集发挥更好的(目前在10.5以上,也许有一天iPhone上)如下:(见的相关苹果文档。)
@property (nonatomic,assign) __weak Row *yCoord;
要报价克里斯·汉森(在这里您可以找到更详细的信息):
“通过前缀一个实例变量的声明与
__weak
,你告诉垃圾收集器,如果是到该对象应该考虑收集的对象的唯一引用。”
我澄清,通过说“如果有一个对象没有非弱引用”。只要最后强引用被移除时,该对象可以被收集,并且所有弱引用将被自动置零。
注意:这不是直接的有关创建弱引用,但也有一个 __strong
属性,但因为Objective-C的对象变量默认情况下强引用,所以一般只用于原料C指针的东西像结构或原语的垃圾收集器将不会当作根,并将于下你收集的,如果你不声明为强。 (尽管缺乏__weak
可引起保持周期和内存泄漏,缺乏__strong
可导致存储器跺脚和发生的非确定性,并且可以是相当困难的追查非常奇怪的和隐袭的错误。)
其他提示
只要改变它分配而不是保留,没有更多的循环引用。
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
弱引用是简单地赋值(除非你正在谈论垃圾收集是蠕虫的整个单独的罐,但不遭受保留周期)。
通常,在可可,Row
将保留Block
对象(通过将它们包括在NSMutableArray的),但Block
不会保留Row
,每个将简单地将其存储在一个实例变量(带有“分配”属性)。
只要它被释放之前Row
是小心以释放每个Block
(即,其dealloc
应松开的NSMutableArray它会释放出块,只要没有其他人具有任何指向它们的指针),然后一切都会被释放为适当
您也可以采取从置零块行引用的预防措施从阵列移除所述entiries之前,是这样的:
- (void) dealloc {
for (Block* b in _blocks) {
b.row = nil;
}
[_blocks release];
[super dealloc];
}
其中_blocks是由块属性引用的实例变量。
在多线程系统中,使用分配创建弱引用可能不安全,特别是当任一对象可以由第三个对象保留,然后用于取消引用另一个对象时。
幸运的是,这通常是一个层次结构问题,包含弱引用的对象只在所引用对象的生命周期内关心它所引用的对象。这是上级<->下级关系的常见情况。
我认为OP评论中的情况与此相对应,行=上级,块=下级。
在这种情况下,我将使用一个句柄来从下级引用上级:
// Superior.h
@class Superior;
@interface SuperiorHandle : NSObject {
@private
Superior* superior_;
}
// note the deliberate avoidance of "nonatomic"
@property (readonly) Superior *superior;
@end
@interface Superior : NSObject {
@private
SuperiorHandle *handle_;
// add one or more references to Subordinate instances
}
// note the deliberate avoidance of "nonatomic"
@property (readonly) SuperiorHandle *handle;
@end
// Superior.m
#import "Superior.h"
@implementation SuperiorHandle
@synthesize
superior = superior_;
- (id)initWithSuperior:(Superior *)superior {
if ((self = [super init])) {
superior_ = superior; // weak reference
}
}
- (void)invalidate {
@synchronized (self) {
superior_ = nil;
}
}
- (Superior *)superior {
@synchronized (self) {
// retain and autorelease is required to prevent dealloc before we're ready, thanks to AndroidDev for pointing out this mistake
return [[superior_ retain] autorelease];
}
}
@end
@implementation Superior
@synthesize
handle = handle_;
- (id)init {
if ((self = [super init])) {
handle_ = [[SuperiorHandle alloc] initWithSuperior:self];
}
return self;
}
- (void)dealloc {
[handle_ invalidate];
[handle_ release];
[super dealloc];
}
@end
// Subordinate.h
@class Superior;
@class SuperiorHandle;
@interface Subordinate : NSObject {
@private
SuperiorHandle *superior_handle_;
}
@property (readonly) Superior *superior;
@end
// Subordinate.m
#import "Subordinate.h"
#import "Superior.h"
@implementation Subordinate
// no synthesize this time, superior's implementation is special
- (id)initWithSuperior:(Superior *)superior {
if ((self = [super init])) {
superior_handle_ = [superior.handle retain];
}
return self;
}
- (void)dealloc {
[superior_handle_ release];
[super dealloc];
}
- (Superior *)superior {
@synchronized (superior_handle_) {
return superior_handle_.superior;
}
}
@end
一些优点:
- 它是线程安全的。没有办法让 Subordination 中包含的弱引用变成无效指针。它可能会变成零,但这没关系。
- 只有对象本身需要了解嵌入的弱引用。所有其他对象都可以将下级视为对上级有常规引用。