GameKit的sendDataToAllPeers没有足够快地发送数据包

时间:2011-04-23 15:35:10

标签: iphone objective-c memory-management memory-leaks bluetooth

我正在尝试通过蓝牙连接发送文件。

感谢我previous post,我已经开始工作了,但这种方法效率不高。

整个文件在发送之前已加载到内存中,这会导致文件问题(并使应用程序崩溃)> ~20 MB。所以我想出了一种新方法,只在特定时间读取我需要的部分文件,从数据创建数据包,发送它们并为文件的每个8KB块重复该过程。

因此,我创建了一个生成数据包的类方法,通知控制器数据包可用(通过协议),并为每个可用数据包重复此过程。

以下是数据包生成器的代码:

+ (void)makePacketsFromFile:(NSString *)path withDelegate:(id <filePacketDelegate>)aDelegate {
    if (![[NSFileManager defaultManager] fileExistsAtPath:path] || aDelegate == nil) return;

    id <filePacketDelegate> delegate;
    delegate = aDelegate;

    const NSUInteger quanta = 8192;
    uint filesize;

    NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
    filesize = [[fileAttributes objectForKey:NSFileSize] intValue];

    int numOfPackets = (int)ceil(filesize/quanta);
    if (numOfPackets == 0) numOfPackets = 1;

    NSLog(@"filesize = %d, numOfPackets = %d or %.3f", filesize, numOfPackets, (float)ceil(filesize/quanta));

    NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];

    int offset = 0;
    int counter = 0;

    while (counter != (numOfPackets + 1)) {
        uint len = (filesize < quanta) ? filesize : quanta;
        if (counter == numOfPackets) {
            len = filesize - offset;
        }
        [handle seekToFileOffset:offset];
        NSData *fileData = [handle readDataOfLength:len];
        file_packet *packet = [[file_packet alloc] initWithFileName:[path lastPathComponent] ofType:0 index:counter];

        packet.packetContents = fileData;
        [fileData release];
        packet.checksum = @"<to be done>";
        packet.numberOfPackets = [NSString stringWithFormat:@"%d", numOfPackets];

        [delegate packetIsReadyForSending:packet];

        [packet release];

        offset += quanta;
        counter++;
    }

    [handle closeFile];

}  

接收和发送文件:

- (void)packetIsReadyForSending:(file_packet *)packet {
    NSData *fileData = [packet dataForSending];
    [self.connectionSession sendDataToAllPeers:fileData withDataMode:GKSendDataReliable error:nil];
}

- (void)sendFileViaBluetooth {
    [file_packet makePacketsFromFile:selectedFilePath withDelegate:self];
}

但是,内存使用量非常大。不是我的预期。

我有点困惑于此,因为我不想将蓝牙共享限制在小于20MB的文件中。

任何帮助表示感谢。

修改:
我一直在考虑这个问题并得出结论,这不是我的代码导致内存分配问题,而是GameKit用于发送数据包的堆栈。

我认为我生成的包过多太快了,我认为GameKit的发送速度不够快。

所以我现在正在考虑一种方法来查看GameKit何时发送了一个数据包,并且只有在GameKit确认它被发送后才生成另一个数据包。

2 个答案:

答案 0 :(得分:2)

您的代码中存在一个明确的内存管理问题:

NSData *fileData = [handle readDataOfLength:len];

fileData未通过NARC(名称中包含new,alloc,retain,copy的方法)获取,因此您不拥有它,因此您不会将其释放。

[fileData release];

糟糕。

至于内存的使用,需要考虑的一件事是,即使你发布packet,你也无法真正告诉-[GKSession sendDataToAllPeers:withDataMode:error:]内部发生了什么。该方法可能在内部创建自动释放的对象,这些对象仅在+makePacketsFromFile:withDelegate:完成执行后才会被释放。我建议你在每次循环迭代中使用一个新的自动释放池:

while (counter != (numOfPackets + 1)) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    uint len = (filesize < quanta) ? filesize : quanta;
    …
    counter++;

    [pool drain];
}

通过执行此操作,该循环内的任何自动释放对象将在每次循环迭代结束时有效释放。

答案 1 :(得分:0)

结帐NSInputStream。有了它,你可以打开一个流,一次只能读出一个字节缓冲区。