Objective-C ARC:使用block作为c++回调是正确的吗?
Objective-C ARC: is it correct to use a block as C++ callback
我有一个用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版本代码中有什么问题
依序排列:
-
-dataWithBytes...
与-dataWithBytesNoCopy...
的区别在于名称。通常,如果你使用一个原始字节数组创建一个NSData, NSData会在内部复制所有字节,这样它就知道它的底层数据不能被独立修改(它也可以管理它想要的内存)。如果你使用...NoCopy
变体,你告诉NSData使用你交给它的缓冲区作为它的存储,而不是分配更多。这样做的好处当然是不使用更多的内存(对于更大的缓冲区可能很重要),但缺点是你需要更加小心你交给它的指针以及之后你对该指针的处理。如果你为freeWhenDone
传递YES,就像你在这里做的那样,你告诉NSData在指针上调用free()
,当它自己被释放时。这假设它是直接从malloc
分配的,并有效地将malloc缓冲区的所有权转移给NSData。 -
您的代码中是否存在保留周期?不,没有明显的。
因此,您看到的内容取决于您对"泄漏"的定义,周围代码中还发生了什么,以及您期望看到的内容。你似乎正在做的是采取字节从pData
,要么将它们复制到NSData或将其所有权转移到NSData,然后使用该数据来创建UIImage
,并将其放入UIImageView
。如何应该处理NSData
的内存管理取决于pData
的所有权,您没有显示。(是谁设计的?谁来解救它?什么时候?)。从图像数据创建UIImage当然会使用内存,但不会"泄漏"内存。如果将其注释掉,那么肯定会使用更少的内存,但显然不会获得图像。:) (NSData本身将总是在-performSelector...
完成后被释放,因为它之后超出了作用域,在你的情况下带着它的缓冲区。)
- 架构决策:返回std::future还是提供回调
- 正在为Xtensa simcall函数编写回调函数
- 如何在C++中使用非静态成员函数作为回调函数
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 用于在回调中调用解析器的设计模式
- 如何使用C++对象的成员函数作为 C 样式回调?
- Java从C++回调到C++回调
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 从不同的 cpp 调用回调函数会导致bad_function_call
- pcap_handler回调仅在使用 NPCAP v0.9991 时包含空数据包
- 不带轮询的 SDL2 事件回调
- C++存储带有可变参数的回调
- 如何使用 Node-addon-API 实现 node-nan 回调
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 访问类C++ C 样式回调
- 处理类内的回调时,必须调用对非静态成员函数的引用
- 如果 C 函数仍然可以间接执行(通过回调函数),那么将它声明为静态函数是否是一种不好的做法?
- 在C++中实现回调
- C++ 事件管理器的回调,使用 std::function 和 std:bind 以及派生类作为参数