我正在研究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
我知道这是一个循环引用。 Apple的文档声明,为了使用循环引用释放对象,我需要一个弱引用而不是一个强引用(一个保留属性),但它没有贯彻并解释我究竟是如何做到的。我计划同时释放和释放一行中的所有块以及行本身。如何在每个块中将弱引用设置为“父”行?
答案 0 :(得分:19)
编辑:由于提问者澄清他没有使用垃圾收集(iPhone目前不支持垃圾收集),我的建议是通过只让其中一个对象保留另一个来避免周期,就像你一样会委托代表。使用属性时,使用“assign”而不是“retain”来实现此目的。例如:
@property (nonatomic,assign) Row *yCoord;
我的答案的其余部分与Objective-C 2.0和GC的“弱引用”有关。
当您使用垃圾收集(10.5+)时,通过在 __weak
前面添加变量声明来创建弱引用。当您分配给该变量时,GC(如果已启用)会跟踪该引用,并且如果对引用对象的所有强引用都消失,则会自动将其归零。 (如果未启用GC,则忽略__weak
属性。)
因此,您可以安全地修改上述答案,以便更好地使用垃圾收集(目前在10.5以上,也许有一天在iPhone上),如下所示:(参见related Apple docs。)
@property (nonatomic,assign) __weak Row *yCoord;
引用Chris Hanson(您可以在哪里找到更多详细信息):
“通过使用
__weak
为实例变量声明加前缀,告诉垃圾收集器,如果它是对象的唯一引用,则该对象应被视为可收集。”
我通过说“如果没有对象的非弱引用”来澄清。一旦删除了最后一个强引用,就可以收集该对象,并且所有弱引用都将自动归零。
注意:这与创建弱引用的 无关,但还有 __strong
属性,但因为Objective-C对象变量默认是强引用,所以它通常仅用于原始C指针,例如垃圾收集器不会将其视为根的结构或基元,如果您不声明,将从您下面收集他们一样坚强。 (缺少__weak
会导致保留周期和内存泄漏,缺少__strong
可能会导致内存踩踏,并且非确定性地发生非常奇怪和隐蔽的错误,并且很难追查。)
答案 1 :(得分:8)
只需将其更改为assign而不是retain,不再使用循环引用。
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
答案 2 :(得分:4)
弱引用只是一个赋值(除非你在谈论垃圾收集,它是一个完全独立的蠕虫,但不会受到保留周期的影响)。
通常,在Cocoa中,Row
将保留Block
个对象(通过将它们包含在NSMutableArray中),但Block
不会保留Row
,每个都只会存储Row
它在一个ivar(带有“assign”属性)。
只要Block
在解除分配之前小心释放每个dealloc
(即,- (void) dealloc {
for (Block* b in _blocks) {
b.row = nil;
}
[_blocks release];
[super dealloc];
}
应该释放NSMutableArray,只要没有其他人拥有块,它就会释放它们任何指向它们的指针)然后一切都将被酌情解除分配。
在从数组中删除entiries之前,您还可以采取预防措施将Block中的行引用归零,如:
{{1}}
其中_blocks是blocks属性引用的ivar。
答案 3 :(得分:3)
在多线程系统中使用assign来创建弱引用可能是不安全的,特别是当任何一个对象可以被第三个对象保留,然后用于取消引用另一个对象时。
幸运的是,这通常是层次结构的问题,包含弱引用的对象只关心它引用的对象生命周期所引用的对象。这是具有优越的从属关系的通常情况。
我认为OP评论中的案例映射到此,Row = Superior,Block = Subordinate。
在这种情况下,我会使用句柄来指代下属的高级人员:
// 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
一些优点: