Почему в ARC течет версия для прямой отправки? (и как я могу это исправить?) - PullRequest
0 голосов
/ 23 мая 2019

У меня есть фрагмент кода, который необходимо оптимизировать, поэтому я выполнил прямую рассылку. Прямая диспетчеризация работает (так как в коде выполняется, делает правильные вещи и не падает), но ARC каким-то образом теряет отслеживание объекта клиента и никогда не освобождается. Стандартная версия отправки также работает, и не утечка. Как я могу исправить версию прямой отправки?

Стандартная версия отправки:

     Client * client;
     client = [Client newClientForServerSocket: serverSocket];

Версия для прямой отправки:

     Client * client;
     Class clientClass = Client.class;
     client = (*IMP_newClientForServerSocket)(clientClass,@selector(newClientForServerSocket:),serverSocket);

(Примечание: странно, если я втыкаю Client.class непосредственно в параметр "self" отправки, отправка завершается сбоем. Может быть, это подсказка.)

Ответы [ 2 ]

4 голосов
/ 23 мая 2019

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;
}
0 голосов
/ 23 мая 2019

Я далеко не уверен, что это сработает, но я бы попробовал следующее:

#import <CoreFoundation/CoreFoundation.h>

и затем, когда вы закончите с client

CFRelease((__bridge CFTypeRef)(client));

Не удивительно, что ARC не работает. Маловероятно, что он был разработан для работы с распределением объектов таким способом. ARC работает на уровне Objective-C, а не на базовом C-компоненте

...