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 или без нее.

Другие вопросы по тегам