beginSheet: заблокировать альтернативу ARC? - PullRequest
6 голосов
/ 02 декабря 2011

Майк Эш создал пример использования блоков для обработки обратных вызовов из листов, что выглядит очень хорошо. Это, в свою очередь, было обновлено для работы с сборщиком мусора пользователем 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 Ответ

14 голосов
/ 04 декабря 2011

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;

означает следующее: castcontextInfo до id (универсальный тип объекта в Objective-C) с приведением __bridge_transfer.Этот акт говорит ARC, что он должен выпустить contextInfo.Поскольку переменная block имеет значение __strong (квалификатор по умолчанию), блок сохраняется, и в конце метода он наконец освобождается.Конечным результатом является то, что block освобождается в конце метода, что является ожидаемым поведением.


В качестве альтернативы, вы можете скомпилировать эту категорию с помощью -fno-objc-arc.Xcode позволяет создавать файлы в одном проекте с поддержкой ARC или без нее.

...