0

我正在开发一个应用程序,它可以通过 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];
}

PGD​​ataManagement 归 PGNetConductor 所有:@property (nonatomic, unsafe_unretained) PGDataManagement *pm;

我尝试了很多但未能解决问题。希望有人能给我一些建议。如果您需要代码或更多信息,请告诉我。谢谢!

4

0 回答 0