Подтвердить что ты не робот

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

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

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

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

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

И, наконец, метод 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 с блоками, которые очистят его и станут понятнее, чем (или даже так же ясно, как) зацикленная версия?

4b9b3361

Ответ 1

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

Стандартный шаблон, используемый во всех 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

Я думаю, что проблема в том, что (вопреки тому, что критики претензии 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

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

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