20

我正在开发一个 iPhone 应用程序。我有一个类对象Row,需要释放该类的许多对象Block。每个Block当前都有一个属性,该属性保留 class 的实例变量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 的文档指出,为了释放具有循环引用的对象,我需要一个弱引用而不是强引用(保留属性),但它没有贯彻并解释我是如何做到这一点的。我计划同时释放和释放行内的所有块以及行本身。如何在我的每个块中设置对其“父”行的弱引用?

4

4 回答 4

19

编辑:由于提问者澄清他没有使用垃圾收集(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 指针,指向诸如结构或原语之类的东西Garbage Collector 不会将其视为根,如果您不将它们声明为强大,则会从您的下方收集。(虽然缺少__weak可能会导致保留周期和内存泄漏,但缺少__strong可能会导致内存踩踏和非常奇怪和隐蔽的错误,这些错误会以非确定性方式发生并且很难追踪。)

于 2009-07-02T04:47:05.740 回答
8

只需将其更改为分配而不是保留,不再循环引用。

@interface Block : UIImageView {
  Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
于 2009-07-02T08:59:56.537 回答
4

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

通常,在 Cocoa 中,Row会保留Block对象(通过将它们包含在 NSMutableArray 中),但Block不会保留Row,每个对象都会简单地将其存储在 ivar 中(具有“分配”属性)。

只要在释放之前Row小心释放每个块Block(即,它dealloc应该释放 NSMutableArray,只要没有其他人有任何指向它们的指针,它就会释放块),那么一切都会被适当地释放。

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

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

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

于 2009-07-02T04:51:33.163 回答
3

在多线程系统中,使用 assign 创建弱引用可能是不安全的,特别是当任何一个对象可以被第三个对象保留,然后用于取消引用另一个对象时。

幸运的是,这通常是一个层次结构的问题,包含弱引用的对象只关心它所引用的对象在被引用对象的生命周期内。这是具有上级<->下级关系的常见情况。

我认为 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

一些优点:

  1. 它是线程安全的。没有办法让 Subordinate 中包含的弱引用成为无效指针。它可能会变为 nil,但没关系。
  2. 只有对象本身需要知道嵌入的弱引用。所有其他对象都可以将 Subordinate 视为具有对 Superior 的常规引用。
于 2011-05-17T21:24:28.783 回答