NSMutableData的内存问题

时间:2013-11-05 05:52:57

标签: ios objective-c encryption memory-leaks automatic-ref-counting

我已将我的项目转换为ARC。后来我发现了一个与我正在加载的PDF相关的内存泄漏。实际发生的事情是,当我回来时,当我的导航控制器弹出PDF视图时,内存无法正常发布。然后我使用Instrument查看代码行导致内存泄漏的原因。

以下我已经解释了发生了什么。

首先,我检查我的pdf是否已加密(已加密)。然后我使用StringEncryption Class

解密它
StringEncryption *crypto = [[StringEncryption alloc]init];
self.stringEncryption = crypto;
[self.stringEncryption setDelegate:self];
NSData *encryptedPDFData = [NSData dataWithContentsOfFile:pdfPath];
[crypto decryptUsingBGProcess:encryptedPDFData key:[key dataUsingEncoding:NSUTF8StringEncoding]];

decryptUsingBGProcess方法如下

@autoreleasepool {{
    CCOptions padding = kCCOptionPKCS7Padding;  
    NSMutableData *decryptedData =[self doCipher:plainText key:aSymmetricKey context:kCCDecrypt padding:&padding ];
    if(!decryptedData)NSLog(@"0001 , EVT,%@, %@,Decryption failed ",[[self class] description],NSStringFromSelector(_cmd));
    if(delegate && [delegate respondsToSelector:@selector(decryptionCompleted:)]){
        if([delegate isKindOfClass:[PDFAnnotationSampleViewController class]] )
            [((PDFAnnotationSampleViewController*)delegate) performSelectorOnMainThread:@selector(decryptionCompleted:) withObject:decryptedData waitUntilDone:NO];
        else [delegate decryptionCompleted:decryptedData];
    }
}

docyper方法如下

- (NSMutableData *)doCipher:(NSData *)plainText key:(NSData *)aSymmetricKey
             context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7
{
    CCCryptorStatus ccStatus = kCCSuccess;
    // Symmetric crypto reference.
    CCCryptorRef thisEncipher = NULL;
    // Cipher Text container.
    NSMutableData * cipherOrPlainText = nil;
    // Pointer to output buffer.
    uint8_t * bufferPtr = NULL;
    // Total size of the buffer.
    size_t bufferPtrSize = 0;
    // Remaining bytes to be performed on.
    size_t remainingBytes = 0;
    // Number of bytes moved to buffer.
    size_t movedBytes = 0;
    // Length of plainText buffer.
    size_t plainTextBufferSize = 0;
    // Placeholder for total written.
    size_t totalBytesWritten = 0;
    // A friendly helper pointer.
    uint8_t * ptr;

    // Initialization vector; dummy in this case 0's.
    uint8_t iv[kChosenCipherBlockSize];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));

    //NSLog(@"doCipher: plaintext: %@", plainText);
    NSLog(@"doCipher: key length: %d", [aSymmetricKey length]);

    plainTextBufferSize = [plainText length];

    //LOGGING_FACILITY(plainTextBufferSize > 0, @"Empty plaintext passed in." );

    NSLog(@"pkcs7: %d", *pkcs7);
    // We don't want to toss padding on if we don't need to
    if(encryptOrDecrypt == kCCEncrypt) {
        if(*pkcs7 != kCCOptionECBMode) {
            if((plainTextBufferSize % kChosenCipherBlockSize) == 0) {
                *pkcs7 = 0x0000;
            } else {
                *pkcs7 = kCCOptionPKCS7Padding;
            }
        }
    } else if(encryptOrDecrypt != kCCDecrypt) {
        NSLog(@"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7 );
    } 

    // Create and Initialize the crypto reference.
    ccStatus = CCCryptorCreate(encryptOrDecrypt,
                               kCCAlgorithmAES128,
                               *pkcs7,
                               (const void *)[aSymmetricKey bytes],
                               kChosenCipherKeySize,
                               (const void *)iv,
                               &thisEncipher
                               );

   //LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem creating the context, ccStatus == %d.", ccStatus );

    // Calculate byte block alignment for all calls through to and including final.
    bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);

    // Allocate buffer.
    bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t) );

    // Zero out buffer.
    memset((void *)bufferPtr, 0x0, bufferPtrSize);

    // Initialize some necessary book keeping.

    ptr = bufferPtr;

    // Set up initial size.
    remainingBytes = bufferPtrSize;

    // Actually perform the encryption or decryption.
    ccStatus = CCCryptorUpdate(thisEncipher,
                               (const void *) [plainText bytes],
                               plainTextBufferSize,
                               ptr,
                               remainingBytes,
                               &movedBytes
                               );

   //LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem with CCCryptorUpdate, ccStatus == %d.", ccStatus );

    // Handle book keeping.
    ptr += movedBytes;
    remainingBytes -= movedBytes;
    totalBytesWritten += movedBytes;

    // Finalize everything to the output buffer.
    ccStatus = CCCryptorFinal(thisEncipher,
                              ptr,
                              remainingBytes,
                              &movedBytes
                              );

    totalBytesWritten += movedBytes;

    if(thisEncipher) {
        (void) CCCryptorRelease(thisEncipher);
        thisEncipher = NULL;
    }

    if (ccStatus == kCCSuccess)
        cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
    else
        cipherOrPlainText = nil;

    if(bufferPtr) free(bufferPtr);

    return cipherOrPlainText;

}


// this added by David
- (NSData*) md5data: ( NSString *) str
{
    const char *cStr = [str UTF8String];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5( cStr, strlen(cStr), result );
    NSString* temp = [NSString  stringWithFormat:
            @"02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
            result[0], result[1], result[2], result[3], result[4],
            result[5], result[6], result[7],
            result[8], result[9], result[10], result[11], result[12],
            result[13], result[14], result[15]
            ];
    return  [NSData dataWithBytes:[temp UTF8String] length:[temp length]];

}

在decrpting之后调用decryptionCompleted:方法使用delegate.That方法如下

-(void)decryptionCompleted:(NSData*)decryptedData{
    /* PDF has decrypted successfully */
    NSLog(@"0403 , EVT,%@, %@,Paper is Decrypted successfuly ",[[self class] description],NSStringFromSelector(_cmd));
    NSMutableData *decryptedMData = [[NSMutableData alloc] initWithData:decryptedData];
    APPDFDocument *pdfFile = [[APPDFDocument alloc] initWithPDFData:decryptedMData information:self.pdfInfo];
    self.passwordEncrypted = [pdfFile isEncrypted];
    NSLog(@"0404 , EVT,%@, %@,Paper is Decrypted paper has password encryption = %@ ",[[self class] description],NSStringFromSelector(_cmd),self.passwordEncrypted?@"YES":@"NO");
    [self loadPDF:pdfFile];
}

这是显示内存泄漏发生位置的地方。请参阅以下有关分析器的屏幕截图。

enter image description here

(这里我缩放了问题区域) enter image description here 有些人可以帮我找出出了什么问题。或者请指导我如何进一步分析

1 个答案:

答案 0 :(得分:0)

试试这样:

 NSMutableData *decryptedMData = [decryptedData mutableCopy]; 

并将自动释放块放在此方法中,如下所示:

 -(void)decryptionCompleted:(NSData*)decryptedData{
@autoreleasepool{
        /* PDF has decrypted successfully */
        NSLog(@"0403 , EVT,%@, %@,Paper is Decrypted successfuly ",[[self class] description],NSStringFromSelector(_cmd));
        NSMutableData *decryptedMData = [[NSMutableData alloc] initWithData:decryptedData];
        APPDFDocument *pdfFile = [[APPDFDocument alloc] initWithPDFData:decryptedMData information:self.pdfInfo];
        self.passwordEncrypted = [pdfFile isEncrypted];
        NSLog(@"0404 , EVT,%@, %@,Paper is Decrypted paper has password encryption = %@ ",[[self class] description],NSStringFromSelector(_cmd),self.passwordEncrypted?@"YES":@"NO");
        [self loadPDF:pdfFile];
}
}