问题
I'm working on an iPhone application. I have an object of class Row
that needs to release numerous objects of the class Block
. Every Block
currently has a property that retains an instance variable of class Row
.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,retain) Row *yCoord;
@end
Every Row
contains an NSMutableArray
of these Blocks.
@interface Row : NSObject {
NSMutableArray *blocks;
}
-(void)addBlock:(Block*)aBlock;
@end
@implementation Row
-(void)addBlock:(Block*)aBlock {
[blocks addObject:aBlock];
aBlock.yCoord = self;
}
@end
I understand that this is a circular reference. Apple's documentation states that in order to deallocate an object with a circular reference I need a weak reference instead of a strong reference (a retain property), but it doesn't follow through and explain how exactly I go about doing so. I plan to release and dealloc all Blocks within a Row as well as the Row itself simultaneously. How do I set up a weak reference within each of my Blocks to their "parent" Row?
回答1:
Edit: Since the asker clarified he's not using garbage collection (iPhone currently does not support it), my advice is to avoid cycles by having only one of the objects retain the other, just as you would do with a delegate. When using properties, use "assign" instead of "retain" to achieve this. For example:
@property (nonatomic,assign) Row *yCoord;
The rest of my answer answer relates to "weak references" in terms of Objective-C 2.0 and GC.
When you're working with garbage collection (10.5+), a weak reference is created by prefixing a variable declaration with __weak
. When you assign to that variable, the GC (if enabled) keeps track of the reference and will zero it out for you automatically if all strong references to the referenced object disappear. (If GC is not enabled, the __weak
attribute is ignored.)
Thus, you can safely modify the above answer to play nicer with garbage collection (currently on 10.5+, and perhaps someday on iPhone) as follows: (See the related Apple docs.)
@property (nonatomic,assign) __weak Row *yCoord;
To quote Chris Hanson (where you can find more detailed information):
"By prefixing an instance variable declaration with
__weak
, you tell the garbage collector that if it's the only reference to an object that the object should be considered collectable."
I'd clarify that by saying "if there are no non-weak references to an object". As soon as the last strong reference is removed, the object may be collected, and all weak references will be zeroed automatically.
Note: This isn't directly related to creating weak references, but there is also a __strong
attribute, but since Objective-C object variables are strong references by default, it is generally used only for raw C pointers to things like structs or primitives that the Garbage Collector will not treat as roots, and will be collected from under you if you don't declare them as strong. (Whereas the lack of __weak
can cause retain cycles and memory leaks, the lack of __strong
can result in memory stomping and really strange and insidious bugs that occur non-deterministically and can be quite difficult to track down.)
回答2:
Just change it to assign instead of retain, no more circular references.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
回答3:
A weak reference is simply an assignment (unless you're talking about Garbage Collection which is a whole separate can of worms, but does not suffer from retain cycles).
Normally, in Cocoa, Row
would retain the Block
objects (by including them in the NSMutableArray), but Block
would not retain Row
, each would simply store it in an ivar (with an "assign" property).
As long as Row
is careful to release each Block
before it is deallocated (ie, its dealloc
should release the NSMutableArray which will release the Blocks as long as no one else has any pointers to them) then everything will be deallocated as appropriate.
You can also take the precaution of zeroing the row reference from Blocks before removing the entiries from the array, something like:
- (void) dealloc {
for (Block* b in _blocks) {
b.row = nil;
}
[_blocks release];
[super dealloc];
}
where _blocks is the ivar referenced by the blocks property.
回答4:
Using assign to create weak references can be unsafe in a multithreaded system, particularly when either object can be retained by a third object, and then used to dereference the other object.
Fortunately, this is often a problem of hierarchy, and the object containing the weak reference only cares about the object it refers to for the referred-to object's lifetime. This is the usual situation with a Superior<->Subordinate relationship.
I think that the case in the OP's comment maps to this, with Row = Superior, Block = Subordinate.
In this case, I would use a handle to refer to the Superior from the 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
Some advantages:
- It's thread safe. There is no way you can have the weak reference contained in Subordinate become an invalid pointer. It may become nil but that is OK.
- Only the objects themselves need to know about the embedded weak reference. All other objects can treat Subordinate as if it has a regular reference to Superior.
来源:https://stackoverflow.com/questions/1072541/i-have-a-circular-reference-how-can-i-create-a-weak-reference-in-objective-c