Objective-C ARC:使用block作为c++回调是正确的吗?

Objective-C ARC: is it correct to use a block as C++ callback

本文关键字:回调 c++ ARC 使用 block 作为 Objective-C      更新时间:2023-10-16

我有一个用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 dataWithBytesdataWithBytesNoCopy有什么区别?

我尝试将这个文件设置为无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版本代码中有什么问题

依序排列:

  1. -dataWithBytes...-dataWithBytesNoCopy...的区别在于名称。通常,如果你使用一个原始字节数组创建一个NSData, NSData会在内部复制所有字节,这样它就知道它的底层数据不能被独立修改(它也可以管理它想要的内存)。如果你使用...NoCopy变体,你告诉NSData使用你交给它的缓冲区作为它的存储,而不是分配更多。这样做的好处当然是不使用更多的内存(对于更大的缓冲区可能很重要),但缺点是你需要更加小心你交给它的指针以及之后你对该指针的处理。如果你为freeWhenDone传递YES,就像你在这里做的那样,你告诉NSData在指针上调用free(),当它自己被释放时。这假设它是直接从malloc分配的,并有效地将malloc缓冲区的所有权转移给NSData。

  2. 您的代码中是否存在保留周期?不,没有明显的。

因此,您看到的内容取决于您对"泄漏"的定义,周围代码中还发生了什么,以及您期望看到的内容。你似乎正在做的是采取字节从pData,要么将它们复制到NSData或将其所有权转移到NSData,然后使用该数据来创建UIImage,并将其放入UIImageView。如何应该处理NSData的内存管理取决于pData的所有权,您没有显示。(是谁设计的?谁来解救它?什么时候?)。从图像数据创建UIImage当然会使用内存,但不会"泄漏"内存。如果将其注释掉,那么肯定会使用更少的内存,但显然不会获得图像。:) (NSData本身将总是在-performSelector...完成后被释放,因为它之后超出了作用域,在你的情况下带着它的缓冲区。)