Возможно ли создать категорию объекта "Блок" в 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
Ответы
Ответ 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 обычно используется для обертывания переменной, а не самого блока.