Objective-C ARC: правильно ли использовать блок в качестве обратного вызова C ++

У меня есть приложение, написанное на C ++ и Objective-C. Часть C ++ получает данные с удаленной камеры и вызывает функцию обратного вызова Objective-C (Block) для отображения. Objective-C включен ARC.

Когда представление дисплея загружено, я устанавливаю блок в часть C ++, когда поступают данные, C ++ будет вызывать этот блок для обновления дисплея.

Код следующим образом:

- (void)viewDidLoad
{
__weak CameraVC *weakSelf = self;
self.callback_func = ^int(int iWidth, int iHeight, int iDataLen, void *pData) {
NSData *data = [NSData dataWithBytes:pData length:iDataLen];
[weakSelf performSelectorOnMainThread:@selector(updateDisplayWithData:)
withObject:data waitUntilDone:YES];
return 0;
};
setDisplayCallback(self.callback_func);
}

- (void)updateDisplayWithData:(NSData *)data
{
self.imageView.image = [UIImage imageWithData:data];
}

setDisplayCallback() является функцией C ++ для установки обратного вызова

Когда приложение запускается, на панели xcode отображается использование памяти приложения, и оно всегда увеличивается, через несколько часов приложение вылетало, думаю, это утечка памяти?

Я попытался прокомментировать код:

// self.imageView.image = [UIImage imageWithData:data];

Утечка памяти прекратилась.

Вопрос 1 Есть ли цикл сохранения, чтобы вызвать эту утечку памяти?

Я попытался заменить код блока из:

 self.callback_func = ^int(int iWidth, int iHeight, int iDataLen, void *pData) {
NSData *data = [NSData dataWithBytes:pData length:iDataLen];
[weakSelf performSelectorOnMainThread:@selector(updateDisplayWithData:)
withObject:data waitUntilDone:YES];
return 0;
};

чтобы:

 self.callback_func = ^int(int iWidth, int iHeight, int iDataLen, void *pData) {
NSData *data = [NSData dataWithBytesNoCopy:pData length:iDataLen freeWhenDone:YES];
[weakSelf performSelectorOnMainThread:@selector(updateDisplayWithData:)
withObject:data waitUntilDone:YES];
return 0;
};

Проблема утечки памяти уменьшена, но все же есть.

вопрос 2 В чем разница между dataWithBytes а также dataWithBytesNoCopy?

ОБНОВИТЬ

Я пытаюсь установить этот единственный файл без ARC, и изменить код:

- (void)viewDidLoad
{
self.callback_func = ^int(int iWidth, int iHeight, int iDataLen, void *pData) {
@autoreleasepool {
NSData *data = [NSData dataWithBytes:pData length:iDataLen];
[self performSelectorOnMainThread:@selector(updateDisplayWithData:)
withObject:data waitUntilDone:YES];
}
return 0;
};
setDisplayCallback(self.callback_func);
}

Использование памяти стабильно. Мне любопытно, в чем проблема в моем коде версии ARC.

2

Решение

В обратном порядке:

  1. Разница между -dataWithBytes... а также -dataWithBytesNoCopy... есть в названии. Обычно, если вы создаете NSData с использованием необработанного байтового массива, NSData копирует все байты внутренне, так что он знает, что его базовые данные не могут быть изменены независимо (и он также может управлять памятью так, как он хочет). Если вы используете ...NoCopy вариант, вы говорите NSData использовать буфер, который вы передаете, в качестве хранилища, и больше не выделять. У этого есть положительный момент, когда мы больше не используем больше памяти (возможно, это важно для больших буферов), но есть и недостаток в том, что вам нужно быть намного более осторожным в отношении того, какой указатель вы ему передаете, и что вы будете делать с этим указателем впоследствии. Если вы переходите в ДА для freeWhenDone как вы делаете здесь, вы говорите NSData для вызова free() на указатель, когда он сам освобожден. Это предполагает, что он был выделен непосредственно из malloc и эффективно передает владение буфером malloc в NSData.

  2. Есть ли в вашем коде цикл сохранения? Нет, очевидного нет.

То, что вы видите, зависит от того, что вы подразумеваете под «утечкой», что еще происходит в окружающем коде и что вы ожидаете увидеть. То, что вы, кажется, делаете, это брать байты из pDataлибо скопировать их в NSData или передать их право собственности на NSData, а затем использовать эти данные для создания UIImageи положить это в UIImageView, Как ты должен обрабатывать управление памятью NSData зависит от владения pData, который вы не показываете. (Кто его использует? Кто должен его освободить? Когда?). Создание UIImage из данных изображения, конечно, будет использовать память, но не будет «вытекать» из нее. Если вы это закомментируете, то наверняка будете использовать меньше памяти, но, очевидно, не получите изображение. 🙂 (сама NSData будет освобождена всегда сразу после -performSelector... завершено, потому что после этого он выходит из области видимости, забирая буфер в обоих случаях.)

1

Другие решения