之前我曾问过类似的问题,但这次情况有点不同。
首先,这次我正在使用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
类型的多余变量。
答案 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)
“可以容纳代码作用于单独的局部变量的东西”看起来像类的实例。