我正在开发一个应用程序,它可以通过 ftp 请求从服务器下载图像然后显示它们。ftp 请求在后台运行。下载完成后,将向视图发送更新消息。
I encountered a memory leak when doing the ftp request which has the following features:
1. The memory leak do not happen every time. May be 1 / 7.
2. If I do the ftp request on the main thread, everything is OK.
3. If I do the ftp request on simulator, everything is OK.
我使用SIMPLEFTP来完成 ftp 工作,并且我做了一些修改来修复我的请求。
在 FtpListService.m 中,该文件用于请求一个文件列表信息(名称、大小、修改日期)。这里发生了内存泄漏(我用“####”突出显示了这一行)。
//This is the method to start a ftp request
- (void)_startReceive
// Starts a connection to download the current URL.
{
BOOL success;
NSURL * url;
CFReadStreamRef ftpStream;
//don't tap receive twice in a row!
assert(self.networkStream == nil);
// First get and check the URL.
self.InputUrl = [self.InputUrl stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
url = [FtpUtil smartURLForString:self.InputUrl];
success = (url != nil);
// If the URL is bogus, let the user know. Otherwise kick off the connection.
if (!success) {
DLog(@"Bad ftp url.");
} else {
// Create the mutable data into which we will receive the listing.
assert(self.listData != nil);
// Open a CFFTPStream for the URL.
ftpStream = CFReadStreamCreateWithFTPURL(NULL, (CFURLRef) url);
assert(ftpStream != NULL);
self.networkStream = (NSInputStream *) ftpStream;
self.networkStream.delegate = self;
[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:RUNLOOPMODEL];
//This timer will be called to terminate the request which is blocked for a customed time.
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:TIMEOUTFTPLIST target:self
selector:@selector(listdealTimeOut:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:RUNLOOPMODEL];
[self.networkStream open];
CFRelease(ftpStream);
}
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
// An NSStream delegate callback that's called when events happen on our
// network stream.
{
connected = @"connected";
switch (eventCode) { //####################### EXC_BAD_ACCESS
case NSStreamEventOpenCompleted: {
//NSLog(@"NSStreamEventOpenCompleted");
} break;
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[LISTDOCBUFFER];
// Pull some data off the network.
bytesRead = [self.networkStream read:buffer maxLength:sizeof(buffer)];
if (bytesRead == -1) {
[self _stopReceiveWithStatus:@"Network read error"];
} else if (bytesRead == 0) {
[self _stopReceiveWithStatus:@"no more data"];
} else {
assert(self.listData != nil);
// Append the data to our listing buffer.
[self.listData appendBytes:buffer length:bytesRead];
[self _parseListData];
}
} break;
case NSStreamEventHasSpaceAvailable: {
//NSLog(@"NSStreamEventHasSpaceAvailable");
assert(NO); // should never happen for the output stream
} break;
case NSStreamEventErrorOccurred: {
DLog(@"NSStreamEventErrorOccurred");
[self _stopReceiveWithStatus:@"Stream open error"];
} break;
case NSStreamEventEndEncountered: {
DLog(@"NSStreamEventEndEncountered");
// ignore
} break;
default: {
DLog(@"default");
assert(NO);
} break;
}
}
在 FtpService.m 中。在这里我可以指定地址和尝试时间来做 ftp 请求:
- (NSArray *)requstServerListInfo:(NSString *)filename tryTime:(int)tryTime
{
NSArray *result = nil;
//Create the request ftp path
NSString* tm = [NSString stringWithFormat:FTPURL];
if(filename != nil)
tm = [NSString stringWithFormat:@"%@%@/",FTPURL,filename];
while (tryTime-- > 0) {
FtpListService *listService = [[FtpListService alloc] initWithUrl:tm];
[listService _startReceive];
//isReceiving will be NO only when : connect error, time out, correctly done job
//I do not really understand the loop, I just know this will cause the request job to begin
while (listService.isReceiving) {
[[NSRunLoop currentRunLoop] runMode:RUNLOOPMODEL beforeDate:[NSDate distantFuture]];
}
//if correctly request, dirArray != nil
if(listService.dirArray == nil) {
[listService release];
continue;
} else {
result = listService.dirArray;
[listService release];
break;
}
}
return result;
}
ftp 作业从 PGNetConductor.m 开始,它是一个单例:
pm = [[PGDataManagement alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
result = [pm startNetWork];
}
PGDataManagement 归 PGNetConductor 所有:@property (nonatomic, unsafe_unretained) PGDataManagement *pm;
我尝试了很多但未能解决问题。希望有人能给我一些建议。如果您需要代码或更多信息,请告诉我。谢谢!