Ios 调度队列和异步RNCryptor
这是一项后续行动 我已经设法用本文描述的方法异步解密了一个下载的大文件(60Mb),Calman在回答中对此进行了更正 基本上是这样的:Ios 调度队列和异步RNCryptor,ios,objective-c,grand-central-dispatch,rncryptor,Ios,Objective C,Grand Central Dispatch,Rncryptor,这是一项后续行动 我已经设法用本文描述的方法异步解密了一个下载的大文件(60Mb),Calman在回答中对此进行了更正 基本上是这样的: int blockSize = 32 * 1024; NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:...]; NSOutputStream *decryptedStream = [NSOutputStream output...]; [cryptedStrea
int blockSize = 32 * 1024;
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:...];
NSOutputStream *decryptedStream = [NSOutputStream output...];
[cryptedStream open];
[decryptedStream open];
RNDecryptor *decryptor = [[RNDecryptor alloc] initWithPassword:@"blah" handler:^(RNCryptor *cryptor, NSData *data) {
NSLog("Decryptor recevied %d bytes", data.length);
[decryptedStream write:data.bytes maxLength:data.length];
if (cryptor.isFinished) {
[decryptedStream close];
// call my delegate that I'm finished with decrypting
}
}];
while (cryptedStream.hasBytesAvailable) {
uint8_t buf[blockSize];
NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
NSData *data = [NSData dataWithBytes:buf length:bytesRead];
[decryptor addData:data];
NSLog("Sent %d bytes to decryptor", bytesRead);
}
[cryptedStream close];
[decryptor finish];
但是,我仍然面临一个问题:在解密之前,所有数据都会加载到内存中。我可以看到一堆“发送X字节到解密器”,然后,控制台中的同一堆“解密器接收X字节”,当我想看到“发送,接收,发送,接收,…”
对于小型(2Mb)文件或模拟器上的大型(60Mb)文件来说,这是很好的;但在真正的iPad1上,由于内存限制,它崩溃了,所以很明显,我不能将此过程保留在我的生产应用程序中
我觉得我需要使用dispatch\u async
将数据发送到解密程序,而不是在while
循环中盲目发送数据,但是我完全迷路了。我试过:
- 在
之前创建我自己的队列,并使用while
dispatch_async(myQueue,^{[decryptor addData:data];})代码>
- 相同,但将整个代码分派到
循环中while
- 相同,但在循环期间调度整个
- 使用
-提供RNCryptor
而不是我自己的队列responseQueue
- 我将while循环的内部封装在@autoreleasepool块中,以强制释放数据。没有它,在while循环完成之前不会发布。(马特·加洛韦(Matt Galloway)在这里有一篇很好的帖子对此进行了解释:
- 调用dispatch_semaphore_wait会阻止执行,直到收到dispatch_semaphore_信号为止。这意味着没有UI更新,如果发送太多,应用程序可能会冻结(因此检查ByteRead>0)
- (IBAction)decryptWithSemaphore:(id)sender {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int total = 0;
int blockSize = 32 * 1024;
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *input = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.rncryptor"];
NSString *output = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.decrypted.pdf"];
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:input];
__block NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:output append:NO];
__block NSError *decryptionError = nil;
[cryptedStream open];
[decryptedStream open];
RNDecryptor *decryptor = [[RNDecryptor alloc] initWithPassword:@"12345678901234567890123456789012" handler:^(RNCryptor *cryptor, NSData *data) {
@autoreleasepool {
NSLog(@"Decryptor recevied %d bytes", data.length);
[decryptedStream write:data.bytes maxLength:data.length];
dispatch_semaphore_signal(semaphore);
data = nil;
if (cryptor.isFinished) {
[decryptedStream close];
decryptionError = cryptor.error;
// call my delegate that I'm finished with decrypting
}
}
}];
while (cryptedStream.hasBytesAvailable) {
@autoreleasepool {
uint8_t buf[blockSize];
NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
if (bytesRead > 0) {
NSData *data = [NSData dataWithBytes:buf length:bytesRead];
total = total + bytesRead;
[decryptor addData:data];
NSLog(@"New bytes to decryptor: %d Total: %d", bytesRead, total);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
}
}
[cryptedStream close];
[decryptor finish];
dispatch_release(semaphore);
}
如果您一次只想处理一个块,那么只在第一个块回调时处理一个块。您不需要信号量,只需要在回调中执行下一次读取。您可能需要
@autoreleasepool
中的readStreamBlock
块,但我认为您不需要它
当我有时间的时候,我可能会将它直接包装到RNCryptor中。我为它打开了。我对拉取请求持开放态度
// Make sure that this number is larger than the header + 1 block.
// 33+16 bytes = 49 bytes. So it shouldn't be a problem.
int blockSize = 32 * 1024;
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:@"C++ Spec.pdf"];
NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:@"/tmp/C++.crypt" append:NO];
[cryptedStream open];
[decryptedStream open];
// We don't need to keep making new NSData objects. We can just use one repeatedly.
__block NSMutableData *data = [NSMutableData dataWithLength:blockSize];
__block RNEncryptor *decryptor = nil;
dispatch_block_t readStreamBlock = ^{
[data setLength:blockSize];
NSInteger bytesRead = [cryptedStream read:[data mutableBytes] maxLength:blockSize];
if (bytesRead < 0) {
// Throw an error
}
else if (bytesRead == 0) {
[decryptor finish];
}
else {
[data setLength:bytesRead];
[decryptor addData:data];
NSLog(@"Sent %ld bytes to decryptor", (unsigned long)bytesRead);
}
};
decryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
password:@"blah"
handler:^(RNCryptor *cryptor, NSData *data) {
NSLog(@"Decryptor recevied %ld bytes", (unsigned long)data.length);
[decryptedStream write:data.bytes maxLength:data.length];
if (cryptor.isFinished) {
[decryptedStream close];
// call my delegate that I'm finished with decrypting
}
else {
// Might want to put this in a dispatch_async(), but I don't think you need it.
readStreamBlock();
}
}];
// Read the first block to kick things off
readStreamBlock();
//确保此数字大于标头+1块。
//33+16字节=49字节。所以这应该不是问题。
int blockSize=32*1024;
NSInputStream*cryptedStream=[NSInputStream inputStreamWithFileAtPath:@“C++Spec.pdf”];
NSOutputStream*decryptedStream=[NSOutputStream outputStreamToFileAtPath:@”/tmp/C++.crypt“append:NO];
[加密流打开];
[解密流打开];
//我们不需要不断地创建新的NSData对象。我们可以重复使用一个。
__块NSMutableData*数据=[NSMutableData数据长度:块大小];
__块RNEncryptor*解密器=nil;
调度块读取流块=^{
[数据集长度:块大小];
NSInteger bytesRead=[cryptedStream read:[data mutableBytes]maxLength:blockSize];
如果(字节读取<0){
//出错
}
else if(bytesRead==0){
[解密程序完成];
}
否则{
[数据集长度:字节读取];
[解密器添加数据:数据];
NSLog(@“已将%ld个字节发送到解密程序”(无符号长)字节读取);
}
};
decryptor=[[RNEncryptor alloc]initWithSettings:kRNCryptorAES256Settings
密码:@“废话”
处理程序:^(RNCryptor*cryptor,NSData*data){
NSLog(@“解密程序接收了%ld个字节),(无符号长)数据。长度);
[decryptedStream write:data.bytes maxLength:data.length];
如果(加密完成){
[解密流关闭];
//给我的代表打电话,告诉他我已完成解密
}
否则{
//可能希望将其放入dispatch_async()中,但我认为您不需要它。
readStreamBlock();
}
}];
//读第一段,开始做一些事情
readStreamBlock();
在花了两天时间试图让我的MBProgress hud用Calman的代码更新其进度后,我想到了以下几点。
使用的内存仍然很低,UI也会更新
- (IBAction)decryptWithSemaphore:(id)sender {
__block int total = 0;
int blockSize = 32 * 1024;
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *input = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.rncryptor"];
NSString *output = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.decrypted.pdf"];
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:input];
__block NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:output append:NO];
__block NSError *decryptionError = nil;
__block RNDecryptor *encryptor=nil;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:input error:NULL];
__block long long fileSize = [attributes fileSize];
[cryptedStream open];
[decryptedStream open];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
encryptor = [[RNDecryptor alloc] initWithPassword:@"12345678901234567890123456789012" handler:^(RNCryptor *cryptor, NSData *data) {
@autoreleasepool {
NSLog(@"Decryptor recevied %d bytes", data.length);
[decryptedStream write:data.bytes maxLength:data.length];
dispatch_semaphore_signal(semaphore);
data = nil;
if (cryptor.isFinished) {
[decryptedStream close];
decryptionError = cryptor.error;
[cryptedStream close];
[encryptor finish];
// call my delegate that I'm finished with decrypting
}
}
}];
while (cryptedStream.hasBytesAvailable) {
@autoreleasepool {
uint8_t buf[blockSize];
NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
if (bytesRead > 0) {
NSData *data = [NSData dataWithBytes:buf length:bytesRead];
total = total + bytesRead;
[encryptor addData:data];
NSLog(@"New bytes to decryptor: %d Total: %d", bytesRead, total);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
HUD.progress = (float)total/fileSize;
});
}
}
}
});
}这是一个好主意,但我认为您不需要信号量。您只需读取回调中的数据,使其自我永久化。我认为实际上并不需要@autoreleasepool,但它是一个