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

Возможно ли создать категорию объекта "Блок" в Objective-C

Я хотел бы добавить функции, создав категорию для Objective-C Blocks.

__block int (^aBlock)(int) = ^int( int n ){
    if( n <= 1 ) return n;
    return aBlock( n - 1 ) + aBlock( n - 2 );
};

Вместо того, чтобы просто разрешить нормальные [aBlock copy], [aBlock retain], [aBlock release], [aBlock autorelease]. Я мог бы сделать что-то вроде:

[aBlock mapTo:anArray];

Возможная категория

@interface UnknownBlockClass (map)

- (NSArray *)mapTo:(NSArray *)array_;

@end
4b9b3361

Ответ 1

@pwc верен тем, что вы не можете создать категорию для класса, который вы не видите.

Однако...

ЧТО Я ЕСМЬ ОБСУЖДАЮТ, ЧТО ВЫ ДОЛЖНЫ ИСПОЛЬЗОВАТЬСЯ СТРОГО КАК УПРАЖНЕНИЕ В ОБУЧЕНИИ И НИКОГДА НЕ ЛЮБЫМ СОРТИРОВАНИЕМ НАСТРОЙКИ ПРОИЗВОДСТВА.

  • Некоторая интроспекция времени выполнения показывает некоторую интересную информацию. Существует ряд классов, содержащих слово "Блок". Некоторые из них выглядят многообещающими: __NSStackBlock, __NSMallocBlock, __NSAutoBlock и NSBlock.
  • Еще одна интроспекция показывает, что многообещающие классы наследуют от NSBlock

Итак, похоже, что любой блок будет каким-то экземпляром или подклассом NSBlock.

Вы можете создать метод для объекта, например:

@implementation Foo
- (void) doFoo {
  //do something awesome with self, a block
  //however, you can't do "self()".  
  //You'll have to cast it to a block-type variable and use that
}
@end

Затем во время выполнения вы можете переместить этот метод в класс NSBlock:

Method m = class_getInstanceMethod([Foo class], @selector(doFoo));
IMP doFoo = method_getImplementation(m);
const char *type = method_getTypeEncoding(m);
Class nsblock = NSClassFromString(@"NSBlock");
class_addMethod(nsblock, @selector(doFoo), doFoo, type);

После этого блоки должны отвечать на сообщение doFoo.

ИСПОЛЬЗУЙТЕ СВОЙ СОБСТВЕННЫЙ РИСК И ТОЛЬКО ДЛЯ ЭКСПЕРИМЕНТА.

Ответ 2

Блок завершает экземпляр типа __NSGlobalBlock__, как показано в следующем фрагменте:

    void (^aBlock)(void) = ^(void) {
        NSLog(@"Hello world");
    };

    // prints "type = __NSGlobalBlock__"
    NSLog(@"type = %@", [aBlock class]);

Чтобы создать категорию класса, компилятор должен уметь видеть оригинальное объявление @interface для класса. Я не могу найти декларацию для __NSGlobalBlock__ и, вероятно, по уважительной причине.

В этой статье и в этой статье содержится некоторая полезная информация о реализации блоков.

В исходный момент, почему бы просто не сделать категорию NSArray для вашего метода mapTo? Это похоже на лучшее место для такого рода функций.

Обновление

Скажем, вы можете добавить категорию к объекту Block. Как бы вы вызвали блок из метода категории? Насколько я понимаю, единственный способ вызвать блок - это оператор () (например, aBlock()). Я не думаю, что есть способ сообщить объекту Block количество и типы параметров. Итак, какие аргументы вы могли бы передать в вызове блока?

Я не рекомендую вам это делать, но следующие работы...

@interface NSObject (BlockExtension)
- (void)foo;
@end

@implementation NSObject (BlockExtension)
- (void)foo
{
    // not sure how else to determine if self is a Block since neither
    // __NSGlobalBlock__ nor any of its superclasses (except NSObject) 
    // are accessible to the compiler
    if ([[[self class] description] isEqual:@"__NSGlobalBlock__"])
    {
        NSLog(@"foo");
        // now what?
        // can't call self(), it doesn't compile
        // how else can I invoke this block?
    }
}
@end

...

void (^aBlock)(void) = ^(void) {
    NSLog(@"Hello world");
};

// prints "foo"
[aBlock foo];

Ответ 3

Дейв ДеЛонг прав, вы не можете добавить категорию в класс, который вы не видите, но поскольку блоки являются подклассами NSBlock, добавив:

@interface NSBlock : NSObject
@end

Теперь вы можете "увидеть" NSBlock и добавить на него категорию, например:

@interface NSBlock (map)
- (NSArray *)mapTo:(NSArray *)array;
@end

@implementation NSBlock (map)
- (NSArray *)mapTo:(NSArray *)array
{
    ...
}
@end

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

Ответ 4

WRONG: A block winds up being an instance of type __NSGlobalBlock__, as seen in the     
following snippet:

int i = 0;
id o = [class self];

void (^aBlock)(void) = ^(void) {

    [o setValue:0];

    NSLog(@"Hello world %d", i);
};

// prints "type = __NSGlobalBlock__" 

// Now it prints __NSStackBlock__ 
// and when moved into HEAP prints __NSMallocBlock__

NSLog(@"type = %@", [aBlock class]);

Только OKAY говорит, что блок завершается экземпляром типа " NSGlobalBlock", если в области нет захваченных переменных, иначе он будет создан в STACK и когда он будет скопирован, который переместит блок в HEAP, и каждая ссылка будет сохранена!

Ответ 5

Простой ответ - нет. Переменная __block - это объект уровня C, а не объект Objective C. Вы можете вызвать [aBlock copy], но это вызывает функцию C block_copy(), а не метод nsobject copy. Таким образом, тип __block - это тип C, поэтому вы не можете добавлять категории.

Коррекция

: __ блок - это идентификатор в компиляторе C, а не typedef.

Я не уверен, достигнет ли это того, что, по вашему мнению, будет, на самом деле, я даже не уверен, что он делает:

__block int (^aBlock)(int) = ^int( int n ){
if( n <= 1 ) return n;
return fib( n - 1 ) + fib( n - 2 );
};

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