ARC предполагает, что метод, имя которого начинается с new
, возвращает счет удержания +1.
Ваша переменная Client *client
по умолчанию __strong
.
В обычном коде отправки, когда ARC назначает ссылку, возвращаемую newClientForServerSocket:
, на client
, он не корректирует счетчик сохранения объекта Client
, поскольку передает право собственности на +1, возвращаемое newClientForServerSocket:
в переменную client
.
В коде прямой отправки ARC не знает, что функция IMP_newClientForServerSocket
возвращает счет удержания +1. Предполагается, что функция возвращает +0 счет удержания. Поэтому, когда ARC назначает ссылку на переменную client
, она сохраняет объект. Таким образом, объект просочился, потому что +1, возвращаемый IMP_newClientForServerSocket
, никогда не освобождается.
Вы можете исправить это, сказав ARC, что IMP_newClientForServerSocket
возвращает счет удержания +1. Документация Clang ARC объясняет, что делать в «Сохраненные возвращаемые значения» :
Функция или метод, который возвращает тип указателя сохраняемого объекта, может быть помечен как возвращающий сохраненное значение, означающее, что вызывающий объект ожидает получить счет удержания +1. Это делается путем добавления атрибута ns_returns_retained
к объявлению функции или метода, например:
id foo(void) __attribute((ns_returns_retained));
- (id) foo __attribute((ns_returns_retained));
Этот атрибут является частью типа функции или метода.
При возврате из такой функции или метода ARC сохраняет значение в точке вычисления оператора возврата, прежде чем покинуть все локальные области.
При получении результата возврата от такой функции или метода ARC высвобождает значение в конце полного выражения, в котором оно содержится, при условии обычной оптимизации для локальных значений.
Вот моя тестовая программа. Без атрибута ns_returns_retained
использование памяти увеличивается без ограничений. Благодаря этому атрибуту использование памяти быстро стабилизируется.
@import Foundation;
@interface Client: NSObject
+ (instancetype _Nonnull)newClient;
@end
@implementation Client
+ (instancetype)newClient { return [self new]; }
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
typedef Client *(*IMP_newClient_type)(id, SEL) __attribute((ns_returns_retained));
IMP_newClient_type IMP_newClient = (IMP_newClient_type)[Client methodForSelector:@selector(newClient)];
while (true) {
Client *c = IMP_newClient(Client.class, @selector(newClient));
[c self]; // do something with c to avoid an unused variable warning
}
}
return 0;
}