简体   繁体   中英

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

I have a App written in C++ and Objective-C. The C++ part receive data from a remote camera and call Objective-C callback(Block) to display. Objective-C is ARC enabled.

When the display view loaded, I set a block to C++ part, when the data is coming, C++ will call this block to update the display.

The code as follows:

- (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() is a C++ function to set a callback.

When the app runs, in the xcode panel shows the app memory usage, and it is always increasing, hours later, the app crashed, I think it is memory leak?

I have try to comment the code:

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

The memory leak stopped.

Question 1 Is there retain cycle to cause this memory leak?

I have try to replace the block code from:

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

to:

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

The memory leak problem reduced, but still have.

Question 2 What's the difference between dataWithBytes and dataWithBytesNoCopy ?

UPDATE

I try to set this single file with no-ARC, and modify the code:

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

The memory usage is stable. I'm curious what's the problem in my ARC version code.

In backwards order:

  1. The difference between -dataWithBytes... and -dataWithBytesNoCopy... is there in the name. Normally, if you create an NSData using a raw byte array, the NSData will copy all the bytes internally so that it knows its underlying data cannot be modified independently (and it can also manage the memory however it wants to). If you use the ...NoCopy variant, you're telling the NSData to use the buffer you hand it as its storage, and not to allocate any more. This has the upside of not using any more memory of course (important for larger buffers maybe) but the downside that you need to be a lot more careful about what pointer you hand it and what you do with that pointer afterwards. If you pass in YES for freeWhenDone as you are doing here, you are telling the NSData to call free() on the pointer when it is itself deallocated. This assumes it was allocated directly from malloc and effectively transfers ownership of a malloc buffer to the NSData.

  2. Is there a retain cycle in your code? No, there is not an obvious one.

So what you're seeing depends on what you mean by "leak", what else is happening in the surrounding code, and what you expect to see. What you appear to be doing is taking the bytes from pData , either copying them into an NSData or transferring their ownership to an NSData, and then using that data to create a UIImage , and putting that into a UIImageView . How you should handle the memory management of the NSData depends on the ownership of pData , which you don't show. (Who mallocs it? Who is expected to free it? When?). Creating the UIImage from the image data will use memory of course, but will not "leak" it. If you comment that out, then you'll for sure use less memory, but obviously won't get the image. :) (The NSData itself will be deallocated always just after the -performSelector... is complete, because it goes out of scope afterwards, taking the buffer with it in both your cases.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM