【问题标题】:Using Objective-C Blocks使用 Objective-C 块
【发布时间】:2009-09-22 19:55:36
【问题描述】:

今天我正在尝试使用 Objective-C 的块,所以我想我会很聪明,并在 NSArray 中添加一些我在其他语言中见过的函数式集合方法:

@interface NSArray (FunWithBlocks)
- (NSArray *)collect:(id (^)(id obj))block;
- (NSArray *)select:(BOOL (^)(id obj))block;
- (NSArray *)flattenedArray;
@end

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

是的 - 就这么一行,太可怕了。我尝试了一些方法来添加换行符和缩进来尝试清理它,但仍然感觉实际算法在所有噪音中丢失了。不过,我不知道这只是语法问题还是我在使用函数式样式方面的相对经验不足。

为了比较,我决定用“老式的方式”来做,只使用循环:

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

IMO 这个版本更容易编写,启动时更易读。

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

【问题讨论】:

    标签: objective-c coding-style


    【解决方案1】:

    使用换行符并将您的呼叫拆分为多行。

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

    你做了什么。很好。

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

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

    即将收集/选择/过滤/展平/映射/任何步骤的每一步都作为一个单独的步骤。这不会比链式方法调用快/慢。

    如果您确实需要将块嵌套在块的一侧,那么请使用全缩进:

    paths = [paths collect: ^(id path) {
        ...
        [someArray select:^(id path) {
            ...
        }];
    }];
    

    就像嵌套的 if 语句等。当它变得过于复杂时,根据需要将其重构为函数或方法。

    【讨论】:

    • 不错的解决方案,虽然我不喜欢多次重新定义paths。我可能最终会根据它们的内容命名几个值,并最终得到一个名为paths 的值。只是我的两分钱。
    • UIView 动画使用 2 个块。
    • 他说模式不是经典。那些确实需要多个块的方法都将块作为最终参数。此外,这些 API 出现在 iOS 4 中,这在这篇文章之后很久。
    【解决方案2】:

    我认为问题在于(与 Python 的批评者所声称的相反;)空白很重要。在更函数化的风格中,复制其他函数式语言的风格似乎是有意义的。编写示例的更 LISP-y 方式可能类似于:

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

    我不会说这比循环版本更清晰。与任何其他工具一样,积木也是一种工具,只有在它们是适合工作的工具时才应使用它们。如果可读性受到影响,我会说它不是这项工作的最佳工具。毕竟,块是对基本命令式语言的补充。如果您真的想要函数式语言的简洁性,请使用函数式语言。

    【讨论】:

      【解决方案3】:

      看起来您正在重新发明高阶消息传递。 Marcel Weiher 在 Objective-C 中的 HOM 方面做了很多工作,你可以在这里找到:

      http://www.metaobject.com/blog/labels/Higher%20Order%20Messaging.html

      【讨论】:

      • 不——HOM 与此完全不同。不过,可能肯定会引起人们的兴趣。
      猜你喜欢
      • 1970-01-01
      • 2017-03-20
      • 1970-01-01
      • 1970-01-01
      • 2014-08-01
      • 2019-09-03
      • 1970-01-01
      • 2017-12-15
      • 2011-12-15
      相关资源
      最近更新 更多