我正在尝试通过蓝牙连接发送文件。
我已经得到了这个工作感谢我以前的帖子,但这种方法不是内存效率高。
整个文件在发送之前被加载到内存中,这对> ~ 20mb的文件产生了问题(并使应用程序崩溃)。所以我想出了一种新方法,在特定时间只读取我需要的部分文件,从数据中创建数据包,发送它们并为每个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确认它已发送时才生成另一个数据包。
你的代码中有一个明显的内存管理问题:
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];
}
通过这样做,循环中的任何自动释放对象将在每次循环迭代结束时有效地释放。
检查NSInputStream。有了它,你可以打开一个流,一次只读出一个字节缓冲区。