Вы стали жертвой ARC, не зная, как NSInvocation
работает, изменяя result
косвенно через другой указатель.Это известная проблема, описанная здесь .
Что происходит, если результирующий объект косвенно становится равным result
, но ARC не знает об этом и никогда не сохранит его.
Не вдаваясь в подробности NSString
- это кластер класса .То, что это эффективно означает, является реализацией под изменениями, основанными на том, как строка создается и используется.Подробности об этом скрыты при взаимодействии с ним в obj-c, и Apple приложила много усилий, чтобы сделать его понятным для разработчиков iOS.Ваш случай несколько особенный.
Обычно вы будете получать:
__NSCFConstantString
(например, @"constant"
) - строковая константа для времени жизни приложения, для вашего случая это случается, что работает, но вы должны никогда полагаться на это NSTaggedPointerString
(например, [[@"a"] mutableCopy] copy]
) - оптимизированная короткая строка с внутренней справочной таблицей. __NSCFString
(например, [@"longString" mutableCopy] copy]
) длинное строковое представление CoreFoundation
.
В любое время NSString
может изменить реализацию ниже, так что вы никогда не должны делать предположений об этом.Случай 3 сразу же выйдет из области видимости после возврата и будет освобожден в следующем цикле выполнения, случай 1 никогда не будет освобожден, случай 2 (?), Но наверняка переживет следующий цикл выполнения.
Так что по сути ARC недостаточно умен, чтобы связать потенциально освобожденный объект с id result
, и вы столкнетесь с проблемами.
Как это исправить?
Используйте один из них:
1.
void *tempResult;
[invocation getReturnValue:&tempResult];
id result = (__bridge id) tempResult;
2.
__unsafe_unretained id tempResult;
[invocation getReturnValue:&tempResult];
result = tempResult;
Редактировать
Как отметил @newacct в своем комментарии getReturnValue:
не выполняет регистрацию weak
указателей, следовательно, неуместно в этом случае.Указатель не будет обнуляться , когда объект будет освобожден.
3.
__weak id tempResult;
[invocation getReturnValue:&tempResult];
result = tempResult;