我正在尝试通过蓝牙连接发送文件。
感谢我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确认它被发送后才生成另一个数据包。
答案 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。有了它,你可以打开一个流,一次只能读出一个字节缓冲区。