保留iOS块的问题

时间:2012-12-21 11:24:32

标签: objective-c ios objective-c-blocks retaincount

之前已经提出了类似的问题,但我无法用这些答案来解决我当前的问题。

情况:

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.

手头的问题是您可能已经想到的保留周期 Object会在addCallback中保留对该块的引用,而阻止doAverage会保留对Object的引用。

使用实例变量是不可能的,因为我想为多个对象重用变量Object。 (临时变量)。
使用局部变量会导致保留计数 并且使用__block CustomType *Object也不起作用,因为无论出于何种原因,Trigger在实际调用回调后最终为nil。

有什么想法吗?

我有一个临时解决方案,但它似乎相当...... hacky。

5 个答案:

答案 0 :(得分:1)

正如已经说过的那样,这个答案相当骇人听闻,如果有人能指出我更好的方向,我会很高兴。
显然,原始数据类型与__block变量相结合可以解决问题,尽管这有点复杂。

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); }];

由于Ptr是基本类型,因此不会保留它,也不必释放它。同时,它假设有问题的对象的地址,从而加倍。

一旦释放了对象,带指针的块也是如此,一切都很好。 调用块后,需要将指针强制转换为相关类型,但这只是一个小问题。

Add当然是可选的,但我不喜欢语法Ptr = Obj = [CustomObject CreateObject];

答案 1 :(得分:1)

有几件事。首先,我想看看你的addCallback:方法。你可能错误地实现了它。例如,如果存储块以供以后使用,则必须复制它。如果不正确,所有投注都将被取消。

  

使用__block CustomType * Object也不起作用,因为   无论什么原因,一旦实际回调,Trigger最终为nil   调用。

因此,如果它是nil,则表示您已将nil分配给Object某处。

答案 2 :(得分:1)

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); }];

泰勒说 - > “但我想要的是对象的'未保留副本',一旦相应的对象被解除分配,它将被'丢失'。”

此代码似乎不会导致retain-cyle,除非您在addCallback([^ {} copy])上使用copy; ...

您的代码中使用的副本到底在哪里?在addCallback里面?如果是这样的话:

addCallback(o) {

o = [o copy];
o();

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

如果你从来没有在任何地方使用过拷贝,没什么可担心的。这一切都发生在堆栈中,这意味着根本不会保留cyles,除非它不是全局的!

如果有零售商,请不要使用__block __weak等来代替它在块的末尾释放任何对象..并记住没有副本没有保留周期..

答案 3 :(得分:0)

如果您的部署目标至少是iOS 5(或OS X 10.7),则可以使用“归零弱引用”:

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

(我使用makeObject而不是CreateObject作为“工厂方法”的名称,因为名称中带有“create”的方法应返回(+1)保留计数对象,而不是自动释放的对象。)

__weak引用不会增加保留计数,因此不会创建保留周期。如果object被销毁,因为最后一个强引用已被删除,则weakSelf设置为nil。在块内部,创建一个强引用,如果它仍然存在则指向该对象,如果它不存在则为nil

如果我理解你的代码是正确的,那么如果对象已被释放,则不会调用回调。在这种情况下,__unsafe_unretained引用就足够了(这也适用于iOS 4):

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

答案 4 :(得分:-3)

尝试将对象声明为

__weak CustomType *Object