我是 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