是否可以将__block变量指向不同的对象,而不更改之前复制的块?

时间:2013-06-19 06:01:40

标签: objective-c scope objective-c-blocks

之前我曾问过类似的问题,但这次情况有点不同。

首先,这次我正在使用ARC(邀请一个全新的问题世界)。 第二:我的代码基本上可以工作,但我想知道我是否能用更少的代码做。

现在,我的代码看起来像这样:

__weak CustomType *Object;
void (^doAverage)(CustomType *, int, int) = ^(CustomType *Trigger, int Val1, int Val2) {
    //Calculations with Trigger
}

CustomType *(^Add)(int, CustomType *) = ^(int Val1, CustomType *NewObject) {
    //Checks prerequisites, and returns either the passed object after adding it to 
    //an array or nil, if the prerequisites haven't been met.
}

现在,更进一步,我正在这样做:

Object = Add(-1, [CustomType makeObjectWithParameters]);
[Object addCallback:^{ doAverage(Object, 56, 57); }];

addCallback内,调用doAverage的传递块会发送copy消息并保存以供日后执行。

这样可行,没有保留周期,如果取消分配对象,则回调会折叠。

但它并不像我想的那样“优雅”。

我想要的是这些内容:

__block __weak CustomType *Object;

CustomType *(^Add)(int, CustomType *) = ^(int Val1, CustomType *NewObject) {
    //Checks prerequisites, and returns either the passed object after adding it to 
    //an array or nil, if the prerequisites haven't been met.
    Object = NewObject;
}

进一步下线:

[Add(-1, [CustomType makeObjectWithParameters]) addCallback:^{ doAverage(Object, 56, 57); }];

但是,如果我这样做,它会将传递给回调的Object更改为我上次调用的Add,这不是我想要的。

基本上我需要set块中Object的值,然后copy作为参数引用,而不是将引用传递给变量本身。

此外,作为第二步,它是完美的,如果我不必首先传递参数Object,但可以在块中使用它。 但是如果我这样做,那么当块最终被调用时,块中的变量将默认为nil(远远超过它所定义的范围的到期)。

所以而不是:

void (^doAverage)(CustomType *, int, int) = ^(CustomType *Trigger, int Val1, int Val2) {
    //Calculations with Trigger
}

我想用:

void (^doAverage)(int, int) = ^(int Val1, int Val2) {
    //Calculations with Object
}

在那个时刻有Object(弱参考)的价值。

两者中的任何一个都有可能吗?

非常感谢。

编辑:

根据以下建议,我重构了我的代码:

typedef BOOL (^stackBlock_t)();
typedef BOOL (^CallBlock_t)(__weak id Object);

-(void) addCallback(CallBlock_t)B {
    stackBlock_t stackBlock = ^{
        __weak id Object = self;
        return B(Object);
    };

    Callback = [stackBlock copy];
}

我试图用以下方法调用此构造:

[Add(-1, CustomType makeObjectWithParameters]) addCallBack:^{ doAverage(Object, 56, 57); }];

但是由于某种原因,Object块没有定义doAverage,我不太明白为什么。毕竟,它是CallBlock_t类型的块,它采用类型为id且名称为Object的参数。它不应该“知道”变量Object吗?

编辑2:经过一些修补(即使用实际类型和东西)后,它现在给我一个不同的错误信息:Incompatible block pointer types sending 'void (^)(void)' to parameter of type 'CallBlock_t',虽然我不是。我希望该块返回BOOL并接受参数Object ...我做错了什么?

编辑3:我可能已找到它。我在addCallback电话中遗漏了一些内容。 我还没有测试它,但看起来,我需要这样称呼它:

[Add(-1, CustomType makeObjectWithParameters]) addCallBack:^(CustomObject *Object){ doAverage(Object, 56, 57); }];

想一想,这很有道理......眼睛看起来很可怕,但这很有道理。它就像一个魅力。我甚至不再需要__block类型的多余变量。

2 个答案:

答案 0 :(得分:1)

该块只是捕获存储位置的引用(强或弱),因此它的实例计数和生命周期与该存储位置相关联(除了在复制块时堆栈指针被堆分配的魔术酱)第一次和参考更新)。

所以知道,为什么Object始终是相同的值应该是相当明显的;所有复制的块指向相同的捕获位置,就像您使用堆栈分配的值在单个函数内完成所有这些操作一样。

在addCallback中,您可以这样做:

- (void)addCallback:(void^(CustomType*))action {
   void(^stackBlock)(void) = ^{
      __weak CustomType *localObj = Object;
      action(localObj);
   };
   self.callback = [stackBlock copy];
}

这将使它捕获调用addCallback时的当前值,该值应与您要求的第一部分匹配。

考虑到所有设置的方式,我认为第二部分是不可能的。只有addCallback捕获Object的时间点值而不使用参数,它无法将其传递给doAverage,因为它甚至不知道它调用的函数。遗憾的是,没有一种干净,简单的方法来进行动态块调度,否则你可以让addCallback获取一个块和一个varargs参数列表,然后使用该对象和提供的参数调用该块。

答案 1 :(得分:0)

“可以容纳代码作用于单独的局部变量的东西”看起来像类的实例