1

我是 Apple 开发的新手(大约 3 个月),现在我所有项目都使用 Xcode 4.5.1 和 ARC。我试图在我的应用程序(也是ARC)中显示一个 mjpeg 视频。由于 mjpeg 编码是基于独立的 jpeg 图像,所以我使用NSImageWell来显示 mjpeg 流的每一帧。但是,当我切换框架时,我注意到内存泄漏

.h 文件:

#import <Cocoa/Cocoa.h>

@interface AMCImagiaTestViewController : NSViewController
- (void) startVideo;
- (void) stopVideo;
@end

.m 文件:

#import "AMCImagiaTestViewController.h"
#include "AMCCommonLib.h"   // My UNIX-like system call library. Used in the thread to call socket functions. Don't mind.
#import "TTImage.h"   // This is NSImage. I only add an NSLog() in -dealloc method

/* forward declaration for a POSIX thread which receives jpeg data.
 This thread receives jpeg images sent by a process of a Linux PC. 
 The socket content is simple enough: standard jpeg data started with 
 FFD8 and end with FFD9 */
void *threadReceiveFrameData(void *arg);

@interface AMCImagiaTestViewController ()
@property (nonatomic, assign) IBOutlet NSImageView *imageVideoFrame;  // the outlet to the image in nib file
@property (nonatomic, assign) pthread_t hdlThread;
/* here are some properties used in thread but I can close or release if I kill it outside its thread loop */
@property (nonatomic, assign) int sockFd;
@property (nonatomic, assign) BOOL isThreadRunning;
@property (nonatomic, retain) NSData *dataForImage;
@property (nonatomic, retain) TTImage *tempImage;
@end

@implementation AMCImagiaTestViewController
...   // some non-important methods

- (void) startVideo
{
    [self stopVideo];
    /* Start the receiving thread */    
    pthread_create(&_hdlThread, NULL, threadReceiveFrameData, (__bridge void*)self);
}

- (void) stopVideo
{
    if (_isThreadRunning)
    {
        pthread_cancel(_hdlThread);
        usleep(100000);
        pthread_join(_hdlThread, NULL);
        _hdlThread = 0;
        if (_sockFd)
        {
            simpleSocketClose(_sockFd);
            _sockFd = 0;
        }
    }
}

...
@end  // end of @implementation AMCImagiaTestViewController


// Now comes to the POSIX thread
void *threadReceiveFrameData(void *arg)
{
    @autoreleasepool {
        AMCImagiaTestViewController *ctrl;
        struct sockaddr_in sockMessage;
        uint8_t buff[60001];
        ssize_t dataLen;

        memset(buff, 0, sizeof(buff));
        ctrl = (__bridge AMCImagiaTestViewController*) arg;

        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        ctrl.isThreadRunning = YES;

        /* socket(). create a UDP socket */
        ctrl.sockFd = simpleSocketCreate_udp();
        if (!ctrl.sockFd)
        {
            SYS_PERROR(); // a package of perror(). don't mind
            goto THREAD_EXIT;
        }

        /* bind(). 22222 is the local port */       
        if (simpleSocketBind_udp(ctrl.sockFd, 22222) < 0)
        {
            SYS_PERROR();
            goto THREAD_EXIT;
        }
        NSLog(@"pthread init OK");

        do
        {
            /* my package of recvfrom(). 
             The first NULL means receive from any IP.
             The second NULL means never timeout (using the select() system call) */
            dataLen = simpleSocketReceiveFrom_udp(ctrl.sockFd, NULL, &sockMessage, buff, sizeof(buff) - 1, NULL);
            if (dataLen > 0)
            {
                //NSLog(@"received: %ld", dataLen);
                ctrl.dataForImage = nil;
                ctrl.dataForImage = [NSData dataWithBytes:buff
                                                   length:dataLen];

                ctrl.tempImage = nil;
                ctrl.tempImage = [[TTImage alloc] initWithData:ctrl.dataForImage];

                /* MEMORY LEAK??? */
                /* These two lines causes memory leaks.
                 I would explain it outside this code block */
                [ctrl.imageVideoFrame setImage:nil];
                [ctrl.imageVideoFrame setImage: ctrl.tempImage];

            }
            else
            {
                SYS_PERROR();
            }
        }
        while (dataLen > 0);




    THREAD_EXIT:
        NSLog(@"pthread exit");
        ctrl.isThreadRunning = NO;
        if (ctrl.sockFd)
        {
            simpleSocketClose(ctrl.sockFd);
            ctrl.sockFd = 0;
        }
        pthread_exit(NULL);
    }
}

“内存泄漏???”的解释 在代码块中:
这一行设置 NSImageWell 以显示图像。如果我继续传输 jpeg 数据,我注意到这个应用程序的内存一直在上升,并且不会显着下降。我添加了“= nil”,试图创建一个 [..release] 方法,但这不起作用。但是,如果我注释掉这两行,就不会再注意到内存泄漏(而且我的视频功能也没有实现......)

有什么建议么?或者我应该切换到非 ARC 模式来检查这个问题?非常感谢!!

1 月 25 日的较新研究:
我创建了另一个没有 ARC的简单项目

NSImage 仍被归类为 TTImage(TT 表示 TestT)为:
.m 文件:

@implementation TTImage
- (id)retain{
    NSLog(@"%08x retain", (unsigned int)self);
    return [super retain];
}
- (oneway void)release{
    NSLog(@"release");
    return [super release];
}
- (void)dealloc{
    NSLog(@"%@ deallocated.", self);
    return [super dealloc];
}
@end

pthread 现在在 App 委托文件中定义:

   ...   // AppDelegate implementations

#define AMCRelease(obj) if(obj) {[(obj) release]; (obj) = nil;}

void *threadReceiveFrameData(void *arg)
{
    @autoreleasepool
    {
        AMCAppDelegate *ctrl;
        NSImage *lastImage;
        struct sockaddr_in sockAddr;
        uint8_t buff[60001];
        ssize_t dataLen;
        NSData *dataInThread = nil;
        TTImage *imageInThread = nil;

        ctrl = (AMCAppDelegate*)arg;
        memset (buff, 0, sizeof(buff));

        ...
        // the same initialization as before

        NSLog(@"pthread init.");

        do {
            dataLen = simpleSocketReceiveFrom_udp(ctrl.sockFd, NULL, &sockAddr, buff, sizeof(buff) - 1, NULL);
            if (dataLen > 0)
            {
                printf("\n");
                AMCRelease(dataInThread);
                dataInThread = [NSData dataWithBytes:buff length:dataLen];
                NSLog(@"%d data rc: %ld", __LINE__, [dataInThread retainCount]);  // Line 100

                if (imageInThread)
                {
                    NSLog(@"%d image pre rc: %ld", __LINE__, [imageInThread retainCount]);  // Line 104
                    //lastImage = imageInThread;
                }               
                AMCRelease(imageInThread);

                imageInThread = [[TTImage alloc] initWithData:dataInThread];
                NSLog(@"%d data rc: %ld", __LINE__, [dataInThread retainCount]);  // Line 110
                NSLog(@"%d image rc: %ld", __LINE__, [imageInThread retainCount]);  // Line 111

                //[ctrl.imageVideoFrame setImage:nil];
                [ctrl.imageVideoFrame setImage:imageInThread];
                NSLog(@"%d image rc: %ld", __LINE__, [imageInThread retainCount]); // Line 115
                //NSLog(@"image last rc: %ld", [lastImage retainCount]);
            }
            else
            {
                SYS_PERROR();
            }
        } while (dataLen > 0);


    THREAD_EXIT:
        AMCRelease(dataInThread);
        AMCRelease(imageInThread);
        if (ctrl.sockFd)
        {
            simpleSocketClose(ctrl.sockFd);
            ctrl.sockFd = 0;
        }
        pthread_exit(NULL);
    }
}

然后我将几个 jpeg 发送到应用程序。以下是输出(忽略每一行的标题):

pthread init.
100 data rc: 1 
110 data rc: 2 
111 image rc: 1 
0067d890 retain    <-- looks OK
115 image rc: 2

100 data rc: 1 
104 image pre rc: 2
release 
110 data rc: 2 
111 image rc: 1 
0067d890 retain   <-- What???  Leaks???
0067d890 retain   <-- What???
00632ba0 retain   <-- looks OK
115 image rc: 2
100 data rc: 1 
104 image pre rc: 2
release 
110 data rc: 2 
111 image rc: 1 
00632ba0 retain  <-- What???
00632ba0 retain  <-- What???
0069d580 retain 
115 image rc: 2
4

1 回答 1

1

好的,我想我可能会解决我的问题。只需在 ARC 项目的 do-while 块中添加 @autoreleasepool 即可。喜欢:

    do {
        @autoreleasepool{
            dataLen = ...

            ... // previous codes need no change

            else
            {
                SYS_PERROR();
            }
        } // release pool ends
    } while (dataLen > 0);

但是,我仍然不知道为什么我后来研究了非 ARC 之一。

于 2013-01-25T08:17:11.557 回答