beginSheet: заблокировать альтернативу ARC?
Майк Эш создал пример использования блоков для обработки обратных вызовов с листов, что кажется очень хорошим. Это было в свою очередь обновлено для работы с сборщиком мусора пользователем Enchilada в другом вопросе SO на beginSheet: block альтернатива?, увидеть ниже.
@implementation NSApplication (SheetAdditions)
- (void)beginSheet:(NSWindow *)sheet modalForWindow:(NSWindow *)docWindow didEndBlock:(void (^)(NSInteger returnCode))block
{
[self beginSheet:sheet
modalForWindow:docWindow
modalDelegate:self
didEndSelector:@selector(my_blockSheetDidEnd:returnCode:contextInfo:)
contextInfo:Block_copy(block)];
}
- (void)my_blockSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
void (^block)(NSInteger returnCode) = contextInfo;
block(returnCode);
Block_release(block);
}
@end
При включении GC это не работает с автоматическим подсчетом ссылок (ARC). Я, будучи новичком в ARC и блоках, не могу заставить его работать. Как мне изменить код, чтобы он работал с ARC?
Я понимаю, что вещи Block_release() нужно удалить, но я не могу обойти ошибки компиляции, связанные с приведением 'void *' к 'void (^)(NSInteger)', запрещенному ARC.
1 ответ
ARC не любит преобразования в void *
Это то, что функции Block_* ожидают в качестве аргументов, потому что ARC не может рассуждать о владении типами, не сохраняемыми. Вы должны использовать приведения мостов, чтобы сообщить ARC, как он должен управлять владением задействованными объектами или что он вообще не должен управлять их владением.
Вы можете решить проблемы ARC, используя код ниже:
- (void)beginSheet:(NSWindow *)sheet
modalForWindow:(NSWindow *)docWindow
didEndBlock:(void (^)(NSInteger returnCode))block
{
[self beginSheet:sheet
modalForWindow:docWindow
modalDelegate:self
didEndSelector:@selector(my_blockSheetDidEnd:returnCode:contextInfo:)
contextInfo:Block_copy((__bridge void *)block)];
}
- (void)my_blockSheetDidEnd:(NSWindow *)sheet
returnCode:(NSInteger)returnCode
contextInfo:(void *)contextInfo
{
void (^block)(NSInteger) = (__bridge_transfer id)contextInfo;
block(returnCode);
}
В первом методе
Block_copy((__bridge void *)block)
означает следующее: приведение block
в void *
используя __bridge
бросать. Этот акт говорит ARC, что он не должен управлять владельцем операнда, поэтому ARC не будет касаться block
управления памятью мудры. С другой стороны, Block_copy()
действительно копирует блок, так что вам нужно сбалансировать эту копию с выпуском позже.
Во втором методе
void (^block)(NSInteger) = (__bridge_transfer id)contextInfo;
означает следующее: приведение contextInfo
в id
(универсальный тип объекта в Objective-C) с __bridge_transfer
бросать. Этот акт говорит ARC, что он должен выпустить contextInfo
, Так как block
переменная __strong (квалификатор по умолчанию), блок сохраняется и, в конце метода, он, наконец, освобождается. Конечным результатом является то, что block
освобождается в конце метода, который является ожидаемым поведением.
Кроме того, вы можете скомпилировать эту категорию с -fno-objc-arc
, Xcode позволяет создавать файлы в одном проекте с поддержкой ARC или без нее.