在iOS上使用RNCryptor加密/解密大文件时出现内存问题 [英] Memory issues when encrypting/decrypting a large file with RNCryptor on iOS

查看:599
本文介绍了在iOS上使用RNCryptor加密/解密大文件时出现内存问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用RNCryptor加密和解密iOS上的大文件(600 + MB)。在 github 上,我找到了示例代码如何在流上异步使用库。此代码类似于Rob Napier关于关于同一主题的问题的答案

I'm trying to use RNCryptor to encrypt and decrypt large files (600+MB) on iOS. On the github I found example code on how to use the library asynchronously on streams. This code is similar to the answer of Rob Napier on a question about this same subject.

然而,虽然我认为我正确实现了代码,但该应用程序使用高达1.5 GB的内存(在iPad 6.1模拟器中)。我认为代码应该阻止应用程序在内存中保留多个数据块?出了什么问题?

However, although I think I implemented the code correctly, the app uses up to 1.5 GB of memory (in the iPad 6.1 simulator). I thought the code was supposed to prevent the app from keeping more than one block of data in-memory? So what is going wrong?

在我的控制器中,我创建了一个'CryptController',我用加密/解密请求发送消息。

In my controller, I create a 'CryptController' which I message with encrypt/decrypt requests.

  // Controller.m
  NSString *password = @"pw123";
  self.cryptor = [[CryptController alloc] initWithPassword:password];

  //start encrypting file
  [self.cryptor streamEncryptRequest:self.fileName andExtension:@"pdf" withURL:[self samplesURL]];

  //wait for encryption to finish
  NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:1];
  do {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:timeout];
  } while (![self.cryptor isFinished]);

在CryptController中我有:

In CryptController I have:

- (void)streamEncryptionDidFinish {
  if (self.cryptor.error) {
    NSLog(@"An error occurred. You cannot trust decryptedData at this point");
  }
  else {
    NSLog(@"%@ is complete. Use it as you like", [self.tempURL lastPathComponent]);
  }
  self.cryptor = nil;
  self.isFinished = YES;
}

- (void) streamEncryptRequest:(NSString *)fileName andExtension:(NSString *)ext withURL:(NSURL *)directory {

  //Make sure that this number is larger than the header + 1 block.
  int blockSize = 32 * 1024;

  NSString *encryptedFileName = [NSString stringWithFormat:@"streamEnc_%@", fileName];
  self.tempURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
  self.tempURL = [self.tempURL URLByAppendingPathComponent:encryptedFileName isDirectory:NO];
  self.tempURL = [self.tempURL URLByAppendingPathExtension:@"crypt"];

  NSInputStream *decryptedStream = [NSInputStream inputStreamWithURL:[[directory URLByAppendingPathComponent:fileName isDirectory:NO] URLByAppendingPathExtension:ext]];
  NSOutputStream *cryptedStream = [NSOutputStream outputStreamWithURL:self.tempURL append:NO];

  [cryptedStream open];
  [decryptedStream open];

  __block NSMutableData *data = [NSMutableData dataWithLength:blockSize];
  __block RNEncryptor *encryptor = nil;

  dispatch_block_t readStreamBlock = ^{
    [data setLength:blockSize];
    NSInteger bytesRead = [decryptedStream read:[data mutableBytes] maxLength:blockSize];
    if (bytesRead < 0) {
      //Throw an error
    }
    else if (bytesRead == 0) {
      [encryptor finish];
    }
    else {
      [data setLength:bytesRead];
      [encryptor addData:data];
      //NSLog(@"Sent %ld bytes to encryptor", (unsigned long)bytesRead);
    }
  };

  encryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
                                           password:self.password
                                            handler:^(RNCryptor *cryptor, NSData *data) {
                                              //NSLog(@"Encryptor received %ld bytes", (unsigned long)data.length);
                                              [cryptedStream write:data.bytes maxLength:data.length];
                                              if (cryptor.isFinished) {
                                                [decryptedStream close];
                                                //call my delegate that i'm finished with decrypting
                                                [self streamEncryptionDidFinish];
                                              }
                                              else {
                                                readStreamBlock();
                                              }
                                            }];

  // Read the first block to kick things off
  self.isFinished = NO;
  readStreamBlock();
}

当我使用分配工具进行分析时,我看到的分配类别一直在增长 malloc 32.50 KB malloc 4.00 KB NSConcreteData NSSubrangeData 。特别是 malloc 32.50 KB 增长很大,超过1 GB。负责的调用者是
[NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:]
对于 NSConcreteData 负责的来电者是
- [NSData(NSData)copyWithZone:]

When I profile using the Allocation Instrument, the allocation categories I see consistently growing are malloc 32.50 KB, malloc 4.00 KB, NSConcreteData and NSSubrangeData. Especially the malloc 32.50 KB grows big, over 1 GB. The responsible caller is [NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:] For NSConcreteData the responsible caller is -[NSData(NSData) copyWithZone:].

当我使用泄漏仪器进行了剖析,没有发现泄漏。

When I profile using the Leaks Instrument, no leaks are found.

我是Objective-C的新手,根据我的理解,新的ARC应该处理分配和释放内存。在搜索与内存相关的任何内容时,我发现的所有信息都假设您不使用ARC(或者在编写时它不存在)。我确定使用ARC,因为当我尝试手动释放内存时,我收到编译错误。

I'm new to Objective-C, and from what I understood, the new ARC is supposed to handle allocation and deallocation of memory. When googling on anything memory related, all the information I find is assuming you don't use ARC (or it didn't exist at time of writing). I sure am using ARC, since I get compile errors saying so when I try to manually deallocate memory.

如果有人能帮助我,我们将不胜感激!如果需要更多信息,我很乐意提供它:)
另外,我是StackOverflow的新手,所以如果有什么我忽略了我应该做的事情,请通知我!

If anyone can help me with this, it would be greatly appreciated! If any more information is needed, I'll be happy to provide it :) Also, I'm new to StackOverflow, so if there's anything I've overlooked that I should have done, kindly inform me!

推荐答案

我终于尝试了这里给出的解决方案,它使用信号量而不是依赖回调来等待流。这完美地运行:)根据Allocations Instrument,内存使用率徘徊在1.1 MB左右。由于信号量语法,它看起来可能不那么整洁,但至少它做了我需要做的事情。

I finally tried the solution given here, which uses semaphores instead of depending on the callback to wait for the stream. This works perfectly :) The memory usage hovers around 1.1 MB according to the Allocations Instrument. It may not look as neat because of the semaphore syntax, but at least it does what I need it to do.

当然,欢迎其他建议:)

Other suggestions are still welcome of course :)

- (void)encryptWithSemaphore:(NSURL *)url {
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

  __block int total = 0;
  int blockSize = 32 * 1024;

  NSString *encryptedFile = [[url lastPathComponent] stringByDeletingPathExtension];
  NSURL *docsURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
  self.tempURL = [[docsURL URLByAppendingPathComponent:encryptedFile isDirectory:NO] URLByAppendingPathExtension:@"crypt"];

  NSInputStream *inputStream = [NSInputStream inputStreamWithURL:url];
  __block NSOutputStream *outputStream = [NSOutputStream outputStreamWithURL:self.tempURL append:NO];
  __block NSError *encryptionError = nil;

  [inputStream open];
  [outputStream open];

  RNEncryptor *encryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
                                                        password:self.password
                                                         handler:^(RNCryptor *cryptor, NSData *data) {
                                                           @autoreleasepool {
                                                             [outputStream write:data.bytes maxLength:data.length];
                                                             dispatch_semaphore_signal(semaphore);

                                                             data = nil;
                                                             if (cryptor.isFinished) {
                                                               [outputStream close];
                                                               encryptionError = cryptor.error;
                                                               // call my delegate that I'm finished with decrypting
                                                             }
                                                           }
                                                         }];
  while (inputStream.hasBytesAvailable) {
    @autoreleasepool {
      uint8_t buf[blockSize];
      NSUInteger bytesRead = [inputStream read:buf maxLength:blockSize];
      if (bytesRead > 0) {
        NSData *data = [NSData dataWithBytes:buf length:bytesRead];

        total = total + bytesRead;
        [encryptor addData:data];

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
      }
    }
  }

  [inputStream close];
  [encryptor finish];  
}

这篇关于在iOS上使用RNCryptor加密/解密大文件时出现内存问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆