2013-03-11 3 views
9

पर आरएनसीप्रिप्टर के साथ बड़ी फ़ाइल को एन्क्रिप्ट/डिक्रिप्ट करते समय मेमोरी समस्याएं IOS पर बड़ी फ़ाइलों (600 + एमबी) को एन्क्रिप्ट और डिक्रिप्ट करने के लिए आरएनसीप्रिप्टर का उपयोग करने का प्रयास कर रहा हूं। the github पर मुझे स्ट्रीम पर असीमित रूप से लाइब्रेरी का उपयोग करने के तरीके पर उदाहरण कोड मिला। यह कोड a question about this same subject पर रोब नेपियर के उत्तर के समान है।आईओएस

हालांकि, हालांकि मुझे लगता है कि मैं कोड सही ढंग से लागू, एप्लिकेशन को स्मृति के 1.5 जीबी तक (आईपैड 6.1 सिम्युलेटर में) का उपयोग करता है। मैंने सोचा था कि कोड को ऐप को स्मृति में डेटा के एक से अधिक ब्लॉक रखने से रोकने के लिए माना जाता था? तो क्या गलत हो रहा है?

मेरी नियंत्रक में, मैं एक 'CryptController' जो एन्क्रिप्ट साथ मैं संदेश/डिक्रिप्ट अनुरोध पैदा करते हैं।

// 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 में मेरे पास है:

- (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 जीबी से अधिक बढ़ता है। जिम्मेदार कॉलर [NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:] NSConcreteData के लिए जिम्मेदार कॉलर -[NSData(NSData) copyWithZone:] है।

जब मैं लीक्स इंस्ट्रूमेंट का उपयोग कर प्रोफ़ाइल करता हूं, तो कोई रिसाव नहीं मिलता है।

मैं ऑब्जेक्टिव-सी के लिए नया हूँ, और से मैं क्या समझ में आया, नई एआरसी आवंटन और स्मृति का आवंटन रद्द करने को संभालने के लिए माना जाता है। जब किसी भी स्मृति से संबंधित हो जाता है, तो मुझे मिली सारी जानकारी यह मानती है कि आप एआरसी का उपयोग नहीं करते हैं (या यह लेखन के समय मौजूद नहीं था)। मुझे यकीन है कि एआरसी का उपयोग कर रहा हूं, क्योंकि जब मैं मैन्युअल रूप से स्मृति को हटाने की कोशिश करता हूं तो मुझे संकलन त्रुटियां मिलती हैं।

अगर कोई मेरी मदद कर सकता है, तो इसकी सराहना की जाएगी! यदि कोई और जानकारी की आवश्यकता है, तो मुझे इसे प्रदान करने में खुशी होगी :) इसके अलावा, मैं स्टैक ओवरफ्लो के लिए नया हूं, इसलिए यदि कुछ भी है तो मैंने अनदेखा किया है कि मुझे करना चाहिए था, कृपया मुझे सूचित करें!

+0

मैं बस से पढ़ रहे हैं और नदियों के लिए लिख, बस सुनिश्चित करें कि स्मृति मुद्दा कहीं नहीं है वहाँ में करने की कोशिश की:

यहाँ कोड है। मेमोरी 1 एमबी से अधिक नहीं हुई, इसलिए समस्या कहीं एन्क्रिप्शन कोड के साथ है। – Johanneke

उत्तर

7

मैं अंत में समाधान here दिया है, जो बजाय कॉलबैक के आधार पर धारा के लिए प्रतीक्षा करने के सेमाफोर का उपयोग करता है की कोशिश की। यह पूरी तरह से काम करता है :) आवंटन उपकरण के अनुसार स्मृति उपयोग 1.1 एमबी के आसपास हो जाता है।यह सैमफोर सिंटैक्स की वजह से साफ दिखता नहीं है, लेकिन कम से कम ऐसा करता है जो मुझे करने की ज़रूरत है।

अन्य सुझाव अभी भी निश्चित रूप से स्वागत कर रहे हैं :)

- (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]; 
} 
0

मैं गलत हो सकता है, लेकिन मैं अपने do...while पाश अक्सर काफी draining से autorelease पूल से बचाता है लगता है।

डिक्रिप्टर को समाप्त करने के लिए आप इस लूप का उपयोग क्यों कर रहे हैं? आपको अपने नियंत्रक को सूचित करने के लिए पूर्णता ब्लॉक का उपयोग करना चाहिए कि इसके बजाय ड्रक्रिप्टर समाप्त हो गया है।

(बीटीडब्ल्यू, एसओ में आपका स्वागत है, आपका प्रश्न वास्तव में अच्छी तरह से पूछा जाता है और इसकी अत्यधिक सराहना की जाती है)।

+0

मैं पाश का उपयोग कर रहा है, क्योंकि मुझे लगता है कि RNCryptor कोड का एक उदाहरण में देखा था, [testAsync] (https://github.com/rnapier/RNCryptor/blob/master/RNCryptorTests/RNCryptorTests.m#L168)। मैं नियंत्रक को कैसे सूचित करूं? और क्या इसका मतलब है कि नियंत्रक को बूलियन मतदान किए बिना बस उस अधिसूचना का इंतजार करना चाहिए? – Johanneke

+0

हाँ, बस अपने नियंत्रक की कुछ विधि को आग लगाने के लिए पूर्णता ब्लॉक की प्रतीक्षा करें। आप NSNotifications का भी उपयोग कर सकते हैं, लेकिन वर्तमान में आपको एक '[self streamEncryptionDidFinish]' मिल गया है जो नौकरी करने लगता है। – Cyrille

+0

ठीक है, धन्यवाद। मैं इसे आज़माउंगा और देख सकता हूं कि यह समस्या हल करता है या नहीं। जब मैं और जानूं, तो मैं यहां वापस पोस्ट करूंगा। – Johanneke

0

मैं फिर से गलत हो सकता है, लेकिन में अपने readStreamBlock, data ब्लॉक के लिए एक पैरामीटर, बल्कि एक __block NSMutableData इसके बाहर घोषित के लिए एक संदर्भ से होना चाहिए। जैसा कि आप देख सकते हैं, RNEncryptor हैंडलर अपना data वैरिएबल प्रदान करता है, जो कि आपने स्वयं घोषित किया है उससे अलग है।

आदर्श रूप में, डाल आपके सभी readStreamBlock सीधे हैंडलर के अंदर, यहां तक ​​कि यह एक ब्लॉक की घोषणा के बिना।

+0

मुझे लगता है कि 'readStreamBlock' में 'डेटा' वैरिएबल है जो दूसरे तरीके के बजाए हैंडलर को पास किया जाता है। 'ReadStreamBlock' में, इनपुटस्ट्रीम से एक ब्लॉक डेटा में पढ़ा जाता है, जिसके बाद' [एन्क्रिप्टर एडडाटा: डेटा] 'कहा जाता है। मैंने सोचा कि इसका मतलब है कि 'डेटा' हैंडलर को यहां पास कर दिया गया है, इसलिए इसे एन्क्रिप्टेड किया जा सकता है और आउटपुटस्ट्रीम का उपयोग करके फ़ाइल में लिखा जा सकता है। और मैं सीधे हैंडलर के अंदर सभी 'रीडस्ट्रीमब्लॉक' डाल सकता हूं, लेकिन फिर पूरी प्रक्रिया शुरू करने के लिए मुझे कुछ कोड डुप्लिकेट करना होगा। जैसा कि यह है कि मैं केवल 'readStreamBlock' को कॉल कर सकता हूं (जैसा कि मैं विधि के अंत में करता हूं)। – Johanneke

+0

लेकिन मैं 'डेटा' चर के साथ खेलने की कोशिश करूंगा और देख सकता हूं कि इससे मदद मिलती है या नहीं। मुझे नहीं लगता कि यह होगा। – Johanneke

+0

हैंडलर केवल 'एनएसडीटा' तर्क स्वीकार करता है, कोई 'एनएसएमयूटेबलडेटा' नहीं ... – Johanneke

4

बस चलाने:

self.cryptorQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSUTF8StringEncoding], NULL); 

dispatch_async(self.cryptorQueue, ^{ 
     readStreamBlock(); 
    }); 

मुसीबत: ढेर बढ़ रहा है और autorelease पुल बफर के लिए रिहाई पर अमल नहीं होंगे।

समाधान: .. इस निष्पादन समाप्त करने के लिए वर्तमान ब्लॉक करने देगा एक ही कतार में async जोड़ें।

__block NSMutableData *data = [NSMutableData dataWithLength:blockSize]; 
__block RNDecryptor *decryptor = nil; 


dispatch_block_t readStreamBlock = ^{ 

    [data setLength:blockSize]; 

    NSInteger bytesRead = [inputStream read:[data mutableBytes] maxLength:blockSize]; 
    if (bytesRead < 0) { 
     // Throw an error 
    } 
    else if (bytesRead == 0) { 
     [decryptor finish]; 
    } 
    else { 

     [data setLength:bytesRead]; 
     [decryptor addData:data]; 
    } 
}; 

decryptor = [[RNDecryptor alloc] initWithPassword:@"blah" handler:^(RNCryptor *cryptor, NSData *data) { 

     [decryptedStream write:data.bytes maxLength:data.length]; 
     _percentStatus = (CGFloat)[[decryptedStream propertyForKey:NSStreamFileCurrentOffsetKey] intValue]/(CGFloat)_inputFileSize; 
     if (cryptor.isFinished) 
     { 
      [decryptedStream close]; 
      [self decryptFinish]; 
     } 
     else 
     { 
      dispatch_async(cryptor.responseQueue, ^{ 
       readStreamBlock(); 
      }); 
      [self decryptStatusChange]; 
     } 

}]; 


// Read the first block to kick things off 

decryptor.responseQueue = self.cryptorQueue; 
[self decryptStart]; 
dispatch_async(decryptor.cryptorQueue, ^{ 
    readStreamBlock(); 
}); 
+0

मैं उसी समस्या में भाग रहा था ओपी और यह मेरे लिए तय किया गया है, विशेष रूप से 'asstc कॉल में' readStreamBlock() 'डाल रहा है। यह थ्रेड को मुख्य लूप पर वापस जाने में सक्षम बनाता है, जो डेटा ब्लॉक के अगले ब्लॉक को पढ़ने से पहले, डेटा ब्लॉक को स्वत: समाप्त करने की अनुमति देता है। धन्यवाद! –

+0

उस कोड के लिए धन्यवाद - यह अच्छी तरह से काम करता है और मेमोरी स्पाइक समस्या हल करता है। लेकिन मुझे अभी भी बड़ी फाइलें डाउनलोड करने और उन्हें एन्क्रिप्ट करने में समस्याएं आ रही हैं क्योंकि आरएनसीआरप्टर के पास धाराओं का समर्थन नहीं है। क्या किसी ने इसे हल किया है? –