繁体   English   中英

我有一个循环参考。 如何在Objective-C中创建弱引用?

[英]I have a circular reference. How can I create a weak reference in Objective-C?

我正在开发一个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的文档声明,为了使用循环引用释放对象,我需要一个弱引用而不是一个强引用(一个保留属性),但它没有贯彻并解释我究竟是如何做到的。 我计划同时释放和释放一行中的所有块以及行本身。 如何在每个块中将弱引用设置为“父”行?

编辑:由于提问者澄清他没有使用垃圾收集(iPhone目前不支持它),我的建议是通过只让一个对象保留另一个来避免周期,就像你对委托一样。 使用属性时,使用“assign”而不是“retain”来实现此目的。 例如:

@property (nonatomic,assign) Row *yCoord;

我的答案的其余部分与Objective-C 2.0和GC的“弱引用”有关。


当您使用垃圾收集(10.5+)时,通过使用__weak为变量声明添加前缀来创建弱引用。 当您分配给该变量时,GC(如果已启用)会跟踪该引用,并且如果对引用对象的所有强引用都消失,则会自动将其归零。 (如果未启用GC,则忽略__weak属性。)

因此,你可以安全地修改上面的答案,以便更好地利用垃圾收集(目前在10.5以上,也许有一天在iPhone上),如下所示:(参见相关的Apple文档 。)

@property (nonatomic,assign) __weak Row *yCoord;

引用Chris Hanson (您可以在哪里找到更多详细信息):

“通过使用__weak为实例变量声明添加前缀,您可以告诉垃圾收集器,如果它是对象的唯一引用,则该对象应被视为可收集。”

我通过说“如果没有对象的非弱引用”来澄清这一点。 一旦删除了最后一个强引用,就可以收集该对象,并且所有弱引用将自动归零。

注意:这与创建弱引用没有直接关系,但是还有一个__strong属性,但由于Objective-C对象变量默认是强引用,因此它通常仅用于指向结构或基元等原始C指针的原始C指针。垃圾收集器不会将其视为根,如果您没有将它们声明为强,则会从您下面收集垃圾收集器。 (虽然缺少__weak会导致保留周期和内存泄漏,但__strong的缺乏会导致内存踩踏,并且非确定性地发生非常奇怪和阴险的错误,并且很难追踪。)

只需将其更改为assign而不是retain,不再使用循环引用。

@interface Block : UIImageView {
  Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end

弱引用只是一个赋值(除非你在谈论垃圾收集,它是一个完全独立的蠕虫,但不会受到保留周期的影响)。

通常,在Cocoa中, Row会保留Block对象(通过将它们包含在NSMutableArray中),但Block不会保留Row ,每个都只是将它存储在ivar中(带有“assign”属性)。

只要Row在解除分配之前小心释放每个Block (即,它的dealloc应该释放NSMutableArray,只要没有其他任何人指向它们就会释放块),那么所有内容都将被解除分配。

在从数组中删除entiries之前,您还可以采取预防措施将Block中的行引用归零,例如:

- (void) dealloc {
    for (Block* b in _blocks) {
        b.row = nil;
    }
    [_blocks release];
    [super dealloc];
}

其中_blocks是blocks属性引用的ivar。

在多线程系统中使用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

一些优点:

  1. 这是线程安全的。 您无法将Subordinate中包含的弱引用变为无效指针。 它可能会变为零,但这没关系。
  2. 只有对象本身需要知道嵌入的弱引用。 所有其他对象都可以将Subordinate视为具有对Superior的常规引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM