使用Objective-C块

Sea*_*ean 17 coding-style objective-c

今天我正在试验Objective-C的块,所以我觉得我很聪明,并在NSArray中添加了一些我在其他语言中看到的功能风格的收集方法:

@interface NSArray (FunWithBlocks)
- (NSArray *)collect:(id (^)(id obj))block;
- (NSArray *)select:(BOOL (^)(id obj))block;
- (NSArray *)flattenedArray;
@end
Run Code Online (Sandbox Code Playgroud)

collect:方法接受一个为数组中的每个项调用的块,并期望使用该项返回某些操作的结果.结果是收集所有这些结果.(如果块返回nil,则不会向结果集添加任何内容.)

select:方法将返回一个只包含原始项的新数组,当作为参数传递给块时,块返回YES.

最后,flattenedArray方法迭代数组的项目.如果一个项是一个数组,它会递归调用flattenedArray并将结果添加到结果集中.如果该项不是数组,则将该项添加到结果集中.一切都完成后返回结果集.

所以现在我有了一些基础设施,我需要一个测试用例.我决定在系统的应用程序目录中找到所有包文件.这就是我想出的:

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) collect:^(id path) { return (id)[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] collect:^(id file) { return (id)[path stringByAppendingPathComponent:file]; }]; }] flattenedArray] select:^(id fullPath) { return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; }];
Run Code Online (Sandbox Code Playgroud)

是的 - 这一切都是一线而且很可怕.我尝试了一些方法来添加换行符和缩进以尝试清理它,但它仍然感觉实际算法在所有噪声中都丢失了.不过,我不知道这只是一种语法问题,还是我使用功能风格的亲戚体验.

为了比较,我决定采用"老式的方式"并使用循环:

NSMutableArray *packagePaths = [NSMutableArray new];
for (NSString *searchPath in NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES)) {
    for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:searchPath error:nil]) {
        NSString *packagePath = [searchPath stringByAppendingPathComponent:file];
        if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:packagePath]) {
            [packagePaths addObject:packagePath];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

IMO这个版本更易于编写,并且更易于启动.

我想这可能是一个不好的例子,但它似乎是一种使用块的合法方式.(我错了?)我是否遗漏了一些关于如何使用块来编写或构造Objective-C代码的东西,这些块可以清理它并使其比循环版本更清晰(甚至更清晰)?

bbu*_*bum 19

使用换行符并跨多行分手.

所有Apple API使用的标准模式是方法或函数应该只接受一个块参数,并且该参数应该始终是最后一个参数.

你做了什么.好.

现在,在编写使用所述API的代码时,请执行以下操作:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES);
paths = [paths collect: ^(id path) {
    ...
}];
paths = [paths collect: ^(id path) {
    ...
}];
paths = [paths select: ^(id path) {
    ...
}];
Run Code Online (Sandbox Code Playgroud)

即你的收集/选择/过滤/展平/地图/每一步作为一个单独的步骤.这将不会比链式方法调用更快/更慢.

如果你确实需要在块的一侧嵌套块,那么在完全缩进的情况下执行此操作:

paths = [paths collect: ^(id path) {
    ...
    [someArray select:^(id path) {
        ...
    }];
}];
Run Code Online (Sandbox Code Playgroud)

就像嵌套的if语句之类.当它变得太复杂时,根据需要将其重构为函数或方法.