Предупреждение Xcode 4.6 ARC для аутентификации Game Center

Это новое предупреждение компилятора, которое появилось только тогда, когда я обновил XCode до версии 4.6. Мой код взят непосредственно из документации Apple (кстати, это мой код iOS 6).

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
    [self setLastError:error];
    if(localPlayer.authenticated){

Предупреждение. Захват localPlayer в этом блоке может привести к циклу удержания


person mevdev    schedule 30.01.2013    source источник


Ответы (2)


Проблема в том, что объект localPlayer сохраняет строгую ссылку на себя — когда localPlayer «захватывается» для использования в блоке authenticationHandler, он сохраняется (когда объекты target-c упоминаются внутри блока, компилятор при вызовах ARC сохраняет для тебя). Теперь, даже когда все другие ссылки на localPlayer перестанут существовать, он все равно будет иметь счетчик сохранения, равный 1, и, следовательно, память никогда не будет освобождена. Вот почему компилятор выдает вам предупреждение.

Обратитесь к нему со слабой ссылкой, например:

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

__weak GKLocalPlayer *blockLocalPlayer = localPlayer;
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
    [self setLastError:error];
    if (blockLocalPlayer.authenticated) {
        ...

Принимая во внимание, что время жизни authenticationHandler и localPlayer тесно связаны (т. е. когда localPlayer освобождается, то же происходит и с authenticationHandler), нет необходимости поддерживать строгую ссылку внутри authenticationHandler. Используя Xcode 4.6, это больше не генерирует упомянутое вами предупреждение.

person Chris McGrath    schedule 30.01.2013
comment
Спасибо! Я понимаю, что блок сохраняет цикл немного больше. Это было прекрасно. - person mevdev; 31.01.2013
comment
Вы также можете не создавать переменную localPlayer и всегда использовать [GKLocalPlayer localPlayer], чтобы не сохранять никаких ссылок. - person tothemario; 27.05.2014

Компилятор просто помогает вам с кодом, который уже был проблемой, просто он не знал об этом раньше.

Вы можете прочитать о циклах сохранения здесь: http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

В основном вам просто нужно изменить свой код на что-то вроде:

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

__weak MyViewController *blockSelf = self;
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
    [blockSelf setLastError:error];
    if(localPlayer.authenticated){
person Eric Reid    schedule 30.01.2013
comment
Кажется, это не работает для меня. Могу ли я просто слабо создать localPlayer? Я выполняю эту аутентификацию в своем AppDelegate, поэтому нет соответствующего viewController. - person mevdev; 31.01.2013
comment
Да, я неправильно прочитал ваш вопрос и подумал, что он жалуется на захват себя. Используйте то, что опубликовал Крис МакГрат. - person Eric Reid; 31.01.2013