Использование блоков Objective-C - PullRequest
       64

Использование блоков Objective-C

17 голосов
/ 22 сентября 2009

Сегодня я экспериментировал с блоками Objective-C, поэтому подумал, что буду умнее и добавлю в NSArray несколько методов сбора в функциональном стиле, которые я видел на других языках:

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

Метод collect: принимает блок, который вызывается для каждого элемента в массиве и должен возвращать результаты некоторых операций с использованием этого элемента. Результатом является сбор всех этих результатов. (Если блок возвращает ноль, к результирующему набору ничего не добавляется.)

Метод select: вернет новый массив, содержащий только элементы из оригинала, которые при передаче в качестве аргумента блоку возвращали YES.

И, наконец, метод flateredArray выполняет итерации по элементам массива. Если элемент является массивом, он рекурсивно вызывает для него flattenArray и добавляет результаты в набор результатов. Если элемент не является массивом, он добавляет элемент в набор результатов. Набор результатов возвращается, когда все закончено.

Так что теперь, когда у меня была некоторая инфраструктура, мне понадобился тестовый пример. Я решил найти все файлы пакетов в системных каталогах приложений. Вот что я придумал:

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

ИМО эту версию было легче написать и она более читабельна для загрузки.

Полагаю, возможно, это был какой-то плохой пример, но мне кажется, что это законный способ использовать блоки. (Я ошибаюсь?) Я что-то упускаю из-за того, как написать или структурировать код Objective-C с блоками, которые очистили бы это и сделали бы его более понятным, чем (или даже столь же понятным, как) зацикленная версия?

Ответы [ 3 ]

19 голосов
/ 23 сентября 2009

Используйте переводы строк и делите вызов на несколько строк.

Стандартный шаблон, используемый во всех API-интерфейсах Apple, состоит в том, что метод или функция должны принимать только один аргумент блока, и этот аргумент всегда должен быть последним аргументом.

Что вы сделали. Хорошо.

Теперь, при написании кода, использующего указанный 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 или тому подобное. Когда он становится слишком сложным, реорганизуйте его в функции или методы, если необходимо.

2 голосов
/ 23 сентября 2009

Я думаю, что проблема в том, что (вопреки утверждениям критиков Python;) пустое пространство имеет значение. В более функциональном стиле кажется, что имеет смысл скопировать стиль других функциональных языков. Более LISP-й способ написания вашего примера может выглядеть примерно так:

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

Я бы не сказал, что это понятнее, чем зацикленная версия. Как и любой другой инструмент, блоки являются инструментом, и их следует использовать только тогда, когда они являются подходящим инструментом для работы. Если читаемость страдает, я бы сказал, что это не лучший инструмент для работы. Блоки, в конце концов, являются дополнением к фундаментально обязательному языку. Если вам действительно нужна лаконичность функционального языка, используйте функциональный язык.

0 голосов
/ 23 сентября 2009

Похоже, вы заново изобретаете сообщения высокого порядка. Марсель Вейхер проделал большую работу над HOM в Objective-C, которую вы можете найти здесь:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...