20

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?

outis
  • 75,655
  • 22
  • 151
  • 221
Tozar
  • 976
  • 9
  • 20
  • 4
    By the way, you're to be commended for your editing and appropriate tagging of your question, especially as a new SO user. Bravo! I appreciate people who take the time to polish things up a bit for the good of the community. – Quinn Taylor Jul 02 '09 at 05:20
  • 2
    Why, thank you! This site is exactly what I've been looking for in every aspect. It only makes sense to take the time to ask a clear question when so many people are willing to jump out and answer it right away. – Tozar Jul 14 '09 at 07:31

4 Answers4

19

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.)

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
  • 1
    Let me clarify that this is for an iPhone application. I don't believe the iPhone uses a GC. – Tozar Jul 02 '09 at 04:49
  • 1
    so instead of declaring Row *yCoord; with @property(nonatomic,retain) Row *yCoord; Do I do: __weak Row *yCoord; But then how do I declare the property? – Tozar Jul 02 '09 at 05:00
  • You can mix @property and __weak: @property(nonatomic,retain) __weak Row *yCoord; – Tim Jul 02 '09 at 05:02
  • Also, be aware that iPhone uses what's called "Modern Runtime", so you should be able to omit the ivar declaration and just use the @property, which eliminates some code duplication. (On the Mac, only 64-bit apps use Modern Runtime — 32-bit apps use "Legacy Runtime" for compatibility reasons.) – Quinn Taylor Jul 02 '09 at 05:07
  • 1
    I appreciate the information but I don't think my problem has been addressed. I would like to implement a similar structure to delegates and subviews, but I have no idea how to declare the Row in the Block class as a weak reference so that the retain count is not incremented. – Tozar Jul 02 '09 at 05:07
  • In light of the code sample you've added, here is my advice: If you don't want a Block to retain its parent Row object, just replace "retain" with "assign" in the @property declaration. (Added to my answer.) – Quinn Taylor Jul 02 '09 at 05:11
  • 1
    Thanks, I believe this is what I was looking for. – Tozar Jul 02 '09 at 05:16
  • I agree with @Tozar. There is no garbage collector in iOS application. So, effectively what happens is that when the reference count reaches 0, it is released from the memory immediately. This is the same from pre-ARC era and has not changed. – Deepak G M Mar 17 '15 at 07:33
  • @QuinnTaylor, Though what you say is correct, you might want to still have member variables instead of property in some cases. For Ex: If you want an object from Base class to be accessed only by subclasses but not the outside world, then you would define it as a member variable rather than property, since properties do not support access specifiers. – Deepak G M Mar 17 '15 at 07:36
8

Just change it to assign instead of retain, no more circular references.

@interface Block : UIImageView {
  Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
Corey Floyd
  • 25,929
  • 31
  • 126
  • 154
4

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.

Peter N Lewis
  • 17,664
  • 2
  • 43
  • 56
  • Is _b the NSMutableArray ivar returned by self.blocks? If so, I'm assuming you mean [_b release] instead. Also, there's no need to use self.blocks inside the class that holds the referencs — just use for (Block* b in _b). It might be less confusing if _b were named "_blocks" or even "blocks". (I realize some people prefer the underscore, but I never use it.) – Quinn Taylor Jul 02 '09 at 14:56
3

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:

  1. 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.
  2. 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.
Brane
  • 810
  • 1
  • 6
  • 13
  • This design will work, but you can't use @synthesize superior in the SuperiorHandle class. @synthesize will create a separate lock for each field (a spin lock to be exact). You need to lock on the same lock you use in 'invalidate' that you do when you return the 'superior_' variable. So you need to implement superior yourself and return @synchronized(self) { returnValue = _superior} return returnValue; – Android Dev Jun 02 '11 at 22:14
  • Actually, maybe this 'Superior' design won't work. What if you call the 'superior' method to get the value for 'Superior', and then another thread invalidates the handle? You are still left with a pointer to the freed memory in the thread that you just obtained the 'superior' value in. Unless, of course, you are assuming none of this is thread safe (but if that were the case, you wouldn't need the @synchronize calls). Unfortunately I don't think this will work at all. You really need a GC environ for this to work, or you have to retain/autorelease the superior value before returning it. – Android Dev Jun 02 '11 at 22:23
  • Re: spin lock. I was not aware of this behaviour, do you have documentation for that? What I've read in Hillegass' book, and what is documented [here](http://stackoverflow.com/questions/588866/atomic-vs-nonatomic-properties) indicates that the lock generated by "synthesize atomic" is @synchronized (self) just as I do manually in invalidate(). – Brane Jun 08 '11 at 16:37
  • Re: another thread dealloc. Good point, I missed part of the memory management. What is required is a retain/autorelease in Subordinate.superior. I have updated the posting. Thanks! – Brane Jun 08 '11 at 16:47
  • Actually I found a cleaner way that also addresses the first comment. Putting the retain/autorelease in the SuperiorHandle.superior instead of Subordinate.superior means the Subordinates (of which there may be many) don't have to know about the memory management. – Brane Jun 08 '11 at 16:56