Передача и вызов динамических блоков в Objective C - PullRequest
5 голосов
/ 13 октября 2011

Как часть структуры модульного теста, я пишу функцию genArray, которая будет генерировать массивы NSArrays, заполненные переданным в блоке генератора. Так что [ObjCheck genArray: genInt] будет генерировать NSArray случайных целых чисел, [ObjCheck genArray: genChar] будет генерировать NSArray случайных символов и т. Д. В частности, я получаю ошибки компилятора в моей реализации genArray и genString, обертка вокруг [ObjCheck genArray: genChar].

Я считаю, что Objective C может динамически управлять блоками, но у меня нет правильного синтаксиса.

ObjCheck.m

+ (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;
}

Когда я пытаюсь скомпилировать, gcc жалуется, что не может выполнить gen(), потому что gen не является функцией. Это имеет смысл, поскольку gen действительно не функция, а id, который должен быть приведен к функции.

Но когда я переписываю подписи для использования id^() вместо id, я также получаю ошибки компилятора. Может ли Objective C обрабатывать произвольно типизированные блоки (это нужно для genArray), или это слишком динамично?

Ответы [ 3 ]

7 голосов
/ 13 октября 2011

Учитывая, что блоки являются объектами, вы можете выполнять преобразование между типами блоков и id в любое время, хотя, если вы приведете блок к неправильному типу блока и вызовете его, вы получите неожиданные результаты (посколькуспособ динамически проверять во время выполнения, что такое «реальный» тип блока *).

Кстати, id^() не является типом.Вы думаете о id(^)().Это может быть источником ошибки компилятора для вас.Вы должны иметь возможность обновить +genArray:, чтобы использовать

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

Естественно, это довольно уродливо.

* На самом деле, есть способ, llvm вставляет строку в кодировке типа obj-c, представляющуютип блока во внутренней структуре блока, но это деталь реализации, и для его извлечения необходимо полагаться на приведение блока к его внутренней структуре реализации.

1 голос
/ 13 октября 2011

Независимо от того, как вы объявляете свои переменные и параметры, ваш код не будет работать.Есть проблема, которая проходит через все ваши ошибки компилятора, и это будет проблемой, даже если вы не делали запутанные вещи с блоками.

Вы пытаетесь добавить символы в NSArray.Вы не можете сделать это.Вы должны будете обернуть их как некий объект Objective C.Поскольку для работы этого примера требуется только одно - чтобы объекты могли быть входными данными для компонентов JoinedByString, вы можете вернуть односимвольные строки NSStrings из g.Тогда для genArray будет работать некоторое количество сигнатур, таких как id ^ ().Я не уверен, как вы заключите это в скобки.Примерно так:

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

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

NSString * - это идентификатор.Чар нет.Вы можете передать NSString * ^ () в id ^ (), но при попытке передать char ^ () в id ^ () вы получите ошибку компилятора.Если вы отказались от некоторой универсальности genArray и объявили, что он принимает char ^ (), он скомпилирует ваш вызов genArray, но при попытке вызвать arrayByAddingObject произойдет ошибка, и аргумент не будет введен в качестве идентификатора.

Кто-то, кто понимает тонкости блочного синтаксиса, может свободно редактировать мой пост, если я получу некоторые незначительные синтаксические ошибки.

Кстати, используйте NSMutableArray в качестве локальной переменной в genArray.Вызов arrayByAddingObject снова и снова будет иметь производительность O (n ^ 2) времени, я думаю.Вы все еще можете объявить тип возвращаемого значения как NSArray, который является суперклассом NSMutableArray, и вызывающие объекты genArray не будут знать разницу.

1 голос
/ 13 октября 2011

Блоки - это функция уровня C, а не ObjC - вы работаете с ними аналогично указателям на функции.Есть статья с очень кратким обзором синтаксиса .(И почти все остальное.)

В вашем примере я бы сделал параметр gen id (^gen)().(Или, возможно, заставить его возвращать void*, использование id будет означать для меня, что gen генерирует объекты ObjC, а не полностью произвольные типы.)

...