在Objective C中传递和调用动态块

时间:2011-10-13 01:05:24

标签: objective-c objective-c-blocks

作为单元测试框架的一部分,我正在编写一个函数genArray,它将生成由传入的生成器块填充的NSArrays。因此[ObjCheck genArray: genInt]将生成随机整数的NSArray,[ObjCheck genArray: genChar]将生成随机字符的NSArray等。特别是,我在genArray和{{的实现中遇到编译器错误1}},genString的包装。

我相信Objective C可以动态操作块,但我没有正确的语法。

ObjCheck.m

[ObjCheck genArray: genChar]

当我尝试编译时,gcc抱怨它无法执行+ (id) genArray: (id) gen { NSArray* arr = [NSMutableArray array]; int len = [self genInt] % 100; int i; for (i = 0; i < len; i++) { id value = gen(); arr = [arr arrayByAddingObject: value]; } return arr; } + (id) genString { NSString* s = @""; char (^g)() = ^() { return [ObjCheck genChar]; }; NSArray* arr = [self genArray: g]; s = [arr componentsJoinedByString: @""]; return s; } ,因为gen()不是函数。这是有道理的,因为gen确实不是函数,而是gen必须强制转换为函数。

但是当我重写签名以使用id而不是id^()时,我也会遇到编译器错误。 Objective C可以处理任意类型的块(genArray需要这个),还是太动态了?

3 个答案:

答案 0 :(得分:7)

鉴于块是对象,您可以随时在块类型和id之间进行转换,但是如果将块转换为错误的块类型并调用它,则会得到意外的结果(因为没有办法在运行时动态检查块的“真实”类型是*)。

BTW,id^()不是一种类型。你在想id(^)()。这可能是您编译器错误的来源。您应该能够更新+genArray:以使用

id value = ((id(^)())(gen))();

当然,那很难看。

*实际上有一种方法,llvm将一个表示块类型的obj-c类型编码字符串插入到块的内部结构中,但这是一个实现细节,并依赖于将块转换​​为其内部实现结构以便提取。

答案 1 :(得分:1)

块是C级功能,而不是ObjC功能 - 您可以使用它们类似于函数指针。有an article with a very concise overview of the syntax。 (还有其他一切。)

在您的示例中,我将gen参数设为id (^gen)()。 (或者可能让它返回void*,使用id意味着我gen生成ObjC对象而不是完全任意类型。)

答案 2 :(得分:1)

无论您如何声明变量和参数,您的代码都无法正常工作。有一个问题贯穿了所有编译器错误,即使你没有用块进行复杂的事情也会出现问题。

您正在尝试将字符添加到NSArray中。你不能这样做。您必须将它们包装为某种Objective C对象。由于您对此示例的唯一要求是对象可以作为componentsJoinedByString的输入,因此您可以从g返回单字符NSStrings。然后像id ^()这样的各种签名将适用于genArray。我不确定你是如何将它括起来的。像这样:

+ (id) genArray: (id^()) gen;

+ (id) genString {
    ...
    NSString * (^g)() = ^() {
        return [NSString stringWithFormat:@"%c", [ObjCheck genChar]];
    };
    ...
}

NSString *是一个id。 char不是。您可以将NSString * ^()传递给id ^(),但是当您尝试将char ^()传递给id ^()时会出现编译器错误。如果你放弃了genArray的一般性并声明接受char ^(),它会编译你对genArray的调用,但是当你试图调用arrayByAddingObject并且参数没有作为id输入时,genArray中会有错误。

如果我遇到一些微妙的语法错误,那么理解块语法错综复杂的人可以随意编辑我的帖子。

顺便说一下,使用NSMutableArray作为genArray中的本地变量。一遍又一遍地调用arrayByAddingObject将具有我想象的O(n ^ 2)时间性能。你仍然可以将返回类型声明为NSArray,它是NSMutableArray的超类,genArray的调用者不会知道它们的区别。