This is a follow-up to Asynchronously decrypt a large file with RNCryptor on iOS
I've managed to asynchronously decrypt a large, downloaded file (60Mb) with the method described in this post, corrected by Calman in his answer.
It basically goes like this:
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];
However, I'm still facing a problem: the whole data is loaded in memory before being decrypted. I can see a bunch of "Sent X bytes to decryptor", and after that, the same bunch of "Decryptor recevied X bytes" in the console, when I'd like to see "Sent, received, sent, receives, ...".
That's fine for small (2Mb) files, or with large (60Mb) files on simulator; but on a real iPad1 it crashes due to memory constraints, so obviously I can't keep this procedure for my production app.
I feel like I need to send the data to the decryptor by using dispatch_async
instead of blindly sending it in the while
loop, however I'm completely lost. I've tried:
- creating my own queue before the
while
, and usingdispatch_async(myQueue, ^{ [decryptor addData:data]; });
- the same, but dispatching the whole code inside of the
while
loop - the same, but dispatching the whole
while
loop - using
RNCryptor
-providedresponseQueue
instead of my own queue
Nothing works amongst these 4 variants.
I don't have a complete understanding of dispatch queues yet; I feel the problem lies here. I'd be glad if somebody could shed some light on this.
If you only want to process one block at a time, then only process a block when the first block calls you back. You don't need a semaphore to do that, you just need to perform the next read inside the callback. You might want an
@autoreleasepool
block inside ofreadStreamBlock
, but I don't think you need it.When I have some time, I'll probably wrap this directly into RNCryptor. I opened Issue#47 for it. I am open to pull requests.