Question

Similar things have been asked before, but I was unable to solve my current problem with any of these answers.

Situation:

CustomType *Object;
BOOL (^doAverage)(CustomType *, int, int) = ^(CustomType *Trigger, int Total, int Pulse) {
    //Calculate Average from Total and Pulse
    Total /= Pulse;
    [Trigger setValue:Total];
};

Object = [CustomType CreateObject]; //Autoreleased Object
[Object addCallback:^{ return doAverage(Object, 56, 32); }];
[Array addObject:Object];       //Adds to collection.

The issue at hand is a retain cycle as you probably already figured.
Object keeps a reference to the block in addCallback, and the block doAverage retains the reference to Object.

Using an instance variable is out of the question, because I want to reuse the variable Object for multiple objects. (Temporary variable).
Using a local variable results in the retain count.
And using __block CustomType *Object doesn't work either, because for whatever reason Trigger ends up as nil once the callback is actually called.

Any ideas?

I have a makeshift solution, but it seems rather...hacky.

Was it helpful?

Solution 2

As already stated, this answer is rather hacky and I'd be very happy if someone could point me in a better direction.
Apparently a primitive datatype in combination with a __block variable does the trick, though this is a bit complicated.

void *Ptr;                //Variable for Block.
__block CustomType *Obj;  //Function variable, mutable by block.
BOOL (^doAverage)(void *, int, int) = ^(void *Trigger, int Total, int Pulse) {
    CustomType *T = (CustomType *)Trigger;  //Conversion
    //Calculate Average from Total and Pulse
    Total /= Pulse;
    [T setValue:Total];
};

//Convenience method.
CustomObject *(^Add)(CustomObject *) = ^(CustomObject *)NewObject {
    [Array addObject:NewObject];
    Obj = NewObject; //Assigns to the local block-Variable.
    return Obj;
};

Ptr = Add([CustomObject CreateObject]); //Creates the Object, and adds it to the array.
[Obj addCallback:^{ return doAverage(Ptr, 56, 32); }];

Since Ptr is a primitive type, it will NOT be retained and does not have to be released. At the same time, it assumes the address of the Object in question and thereby doubles as it.

Once the object is released, so is the block with the pointer and everything is good. Once the block is called, the pointer needs to be cast to the type in question, but that's only a minor problem.

Add is optional of course, but I don't like the syntax Ptr = Obj = [CustomObject CreateObject];

OTHER TIPS

Several things. First, I would like to see your addCallback: method. It's possible that you've implemented it incorrectly. For example, if you store a block for use later, you must copy it. If it's incorrect, all bets are off on the rest of the stuff.

And using __block CustomType *Object doesn't work either, because for whatever reason Trigger ends up as nil once the callback is actually called.

So if it's nil, then that means you assigned nil to Object somewhere.

CustomType *Object;
BOOL (^doAverage)(CustomType *, int, int) = ^(CustomType *Trigger, int Total, int Pulse) {
//Calculate Average from Total and Pulse
Total /= Pulse;
[Trigger setValue:Total];
};

Object = [CustomType CreateObject]; //Autoreleased Object
[Object addCallback:^{ return doAverage(Object, 56, 32); }];

Taylor said -> "What I want however is a 'non-retained copy of the object' in question, which will be 'lost' once the corresponding object is deallocated."

This code doesn't seem to cause a retain-cyle unless you use copy on or inside of addCallback([^{}copy]);..

Where is it exactly the copy is used in your code? Inside of addCallback? if this is like:

addCallback(o) {

o = [o copy];
o();

then do a [o release]; when you done with a block object.. do not release it in dealloc()
} 

if you have never used copy anywhere, nothing to be worried about.. It all happens in the stack that means no retain cyles at all unless it is not a global one!

In case there is a retail-cyle, do not use __block __weak etc instead do release whatever object it is in the end of the block.. and bear in mind that no copy no retain cycle..

If your deployment target is at least iOS 5 (or OS X 10.7), you can use "zeroing weak references":

CustomType *object = [CustomType makeObject];
__weak CustomType *weakObject = object;
[object addCallback:^{
    CustomType *strongObject = weakObject;
    if (strongObject)
        return doAverage(weakObject, 56, 32);
    else
        return 0;
}];

(I have used a makeObject instead of CreateObject for the name of the "factory method", because methods with "create" in their name are expected to return a (+1) retain count object, not an autoreleased object.)

The __weak reference does not increment the retain count, therefore no retain cycle is created. If the object is destroyed because the last strong reference to it is gone, then weakSelf is set to nil. Inside the block a strong reference is created, which either points to the object, if it still exists, or is nil, if it does not exist anymore.

If I understand you code correctly, the callback will not be called if the object has been released. In that case a __unsafe_unretained reference is sufficient (which works also on iOS 4):

CustomType *object = [CustomType makeObject];
__unsafe_unretained CustomType *unsafeObject = object;
[object addCallback:^{
    return doAverage(unsafeObject, 56, 32);
}];

Try declaring the Object as

__weak CustomType *Object
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top