【问题标题】:Objective-C ARC: is it correct to use a block as C++ callbackObjective-C ARC:使用块作为 C++ 回调是否正确
【发布时间】:2014-05-20 01:32:50
【问题描述】:

我有一个用 C++ 和 Objective-C 编写的应用程序。 C++ 部分从远程摄像头接收数据并调用 Objective-C 回调(块)进行显示。 Objective-C 启用了 ARC。

显示视图加载时,我给C++部分设置了一个block,当数据到来时,C++会调用这个block来更新显示。

代码如下:

- (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;
 };

内存泄漏问题减少了,但还是有。

问题2dataWithBytesdataWithBytesNoCopy有什么区别?

更新

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

【问题讨论】:

    标签: c++ objective-c memory-leaks automatic-ref-counting


    【解决方案1】:

    倒序:

    1. -dataWithBytes...-dataWithBytesNoCopy... 之间的区别在于名称。通常,如果您使用原始字节数组创建 NSData,则 NSData 将在内部复制所有字节,以便它知道不能独立修改其底层数据(并且它也可以根据需要管理内存)。如果您使用...NoCopy 变体,您是在告诉 NSData 使用您提供的缓冲区作为其存储空间,而不是再分配任何内容。当然,这具有不使用更多内存的好处(对于更大的缓冲区可能很重要),但不利的一面是,您需要更加小心您交给它的指针以及之后您对该指针做什么。如果您在此处为 freeWhenDone 传递 YES,则您是在告诉 NSData 在指针本身被释放时调用指针上的 free()。这假设它是直接从malloc 分配的,并且有效地将 malloc 缓冲区的所有权转移到 NSData。

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

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

    【讨论】:

    • 嗨,Ben,pData 被分配到 C++ 部分。当数据到来时,C++ 会将内存分配给 pData 并调用此回调(Objective-C 块),当回调完成时,C++ 部分负责释放它。当应用程序运行时,XCode 会显示模拟器内存​​使用情况,并且一直在增加。几个小时后,它坠毁了。 C++ 部分在 android 和 iOS 上都运行,在 android 上运行良好,所以我认为问题出在 iOS 上。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多