0

来月に予定されているプロジェクトに、githubtにあるNSData +Base64opensrcライブラリを使用しています。私はプロファイリング、分析、最適化を始めたばかりで、そのコードに起因するリークを発見しました。NSZombieとすべてのデバッグ方法とトライキャッチを無効にしました。これは、以前にそれらがメモリをリークすることがあることを発見したためです。リークが特定される正確な線は次のとおりです。

- (NSString *)base64EncodedString        
{
    return [self base64EncodedStringWithWrapWidth:0];
}

そして分解:

+0x0    pushl                          %ebp
+0x1    movl                           %esp, %ebp
+0x3    subl                           $24, %esp
+0x6    calll                          -[NSData(Base64) base64EncodedString]+0xb
+0xb    popl                           %eax
+0xc    movl                           +86389(%eax), %eax
+0x12   movl                           %eax, +4(%esp)
+0x16   movl                           +8(%ebp), %eax
+0x19   movl                           %eax, (%esp)
+0x1c   movl                           $0, +8(%esp)
+0x24   calll                          DYLD-STUB$$objc_msgSend   // 100% leak
+0x29   addl                           $24, %esp
+0x2c   popl                           %ebp
+0x2d   ret       

ライブラリまたはコードの他の部分を適切に使用していないことが私のせいである可能性があるため、作成者に連絡していません。

特定の方法を使用する場合(一度だけ使用する場合)、暗号化ルーチンの後に使用します。

+ (NSString *) encryptString:(NSString *)plaintext withKey:(NSString *)key 
{    
    // Convert string-to-be-encrypted to Data
    NSData *inData = [Miscellaneous utf8string2data:plaintext];
    // Encrypt, Encode, Return
    return [[self encryptData:inData withKey:key] base64EncodedString];
}    

しかし、メモリがリークしている実際のポイントは、メソッドによって呼び出されているメソッドにあるように感じます。

base64EncodedStringWithWrapWidth

正確な行は次のとおりです。

outputBytes = realloc(outputBytes, outputLength);
    NSString *result = [[NSString alloc] initWithBytesNoCopy:outputBytes length:outputLength encoding:NSASCIIStringEncoding freeWhenDone:YES];

だから私の質問は次のとおりだと思います:1)このライブラリを使用して同様の動作を観察した人はいますか2)割り当てが不適切な文字列を使用してリークを引き起こしている可能性があります3)それを解決する方法を知っている人、またはこれを置き換えるために使用できる別のライブラリ?

ありがとう!

編集:Instrumentsがリークコードとして特定する上記の行を次のように変更しました:

 outputBytes = realloc(outputBytes, outputLength);
 NSString *result = [[NSString alloc] initWithBytes:outputBytes length:outputLength encoding:NSASCIIStringEncoding];
 free(outputBytes);

しかし、私はまだそのラインからリークを取得します。ObjectiveCのmalloc/realloc / freeに問題はありますか?

編集2:

Bytes Used  # Leaks     Symbol Name
    512 Bytes    5.2%   2       thread_start
    512 Bytes    5.2%   2        _pthread_start
    512 Bytes    5.2%   2         __NSThread__main__
    512 Bytes    5.2%   2          -[NSThread main]
    512 Bytes    5.2%   2           -[Sync get]
    512 Bytes    5.2%   2            -[Request DoRequest]
    512 Bytes    5.2%   2             -[Request encryptMessage:]
    512 Bytes    5.2%   2              +[AES256 encryptString:withKey:]
    512 Bytes    5.2%   2               -[NSData(Base64) base64EncodedString]
    512 Bytes    5.2%   2                -[NSData(Base64) base64EncodedStringWithWrapWidth:]
    512 Bytes    5.2%   2                 -[NSPlaceholderString initWithBytes:length:encoding:]
    512 Bytes    5.2%   2                  CFStringCreateWithBytes
    512 Bytes    5.2%   2                   __CFStringCreateImmutableFunnel3
    512 Bytes    5.2%   2                    _CFRuntimeCreateInstance
    512 Bytes    5.2%   2                     CFAllocatorAllocate
    512 Bytes    5.2%   2                      __CFAllocatorSystemAllocate

これが誤った警告であるかどうかを識別するのに役立つかどうかはわかりませんが、何もフィルタリングしないことにより、原因はCFAllocatorSystemAllocateのように見えます。

+0x13   calll                          DYLD-STUB$$malloc_zone_malloc
+0x18   addl                           $8, %esp

だから私はこれが本当にリークであるかどうかを予約し始めています。ただし、エミュレーターとiPadの両方でデバッグすると同じ結果になります。ARCが使用されており、オブジェクトを参照する代わりにコピーしようとしています。

解決策:リークを発見しました。これは、間違った種類のキャストを使用して不適切に実装されたメソッドにありました(愚かな私はこれを初めて見ませんでした)

return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                               (__bridge_retained CFStringRef)string,
                                                               NULL,
                                                               (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
                                                               CFStringConvertNSStringEncodingToEncoding(encoding));

ここでbridge_retainはARCを1つインクリメントしたため、リリースされていませんでした。そのため、警告が表示されていました。おかしなことに、何が悪いのか気付く前に、それは私が最後に見たコードでした。ご協力ありがとうございました

4

1 に答える 1

1

あなたはおそらくあなたの分析では、リークは再割り当てされた場所から来ているということは正しいでしょうoutputBytes。ただし、リークの「本当の」原因はoutputBytes、最終的には解放されないことに注意してください。問題は、なぜそれが解放されないのかということです。

次の行を見ると、それが指す文字列の内容をコピーせずに直接outputBytesフィードされているので、それ以降、メモリブロックの所有権を取得するNSStringことが作者の意図だったと思います。それを解放する責任。参照カウンターがゼロになると、このブロックを解放します。コードの次の数行は、が自動解放されることを示しています。NSStringoutputBytesNSStringNSStringNSString

#if !__has_feature(objc_arc)
[result autorelease];
#endif

が自動解放される場合NSStringは、すべてが正常である必要があります。所有権はNSString自動解放プールによって取得され、release使用するときに自分で所有する必要はありません。上記のコードスニペットはautorelease、ARC(自動参照カウント)なしでコードをコンパイルしている場合にのみ呼び出されることも示しています。outputBytesしたがって、コードは正しいと思います。したがって、リークされる可能性がある唯一の理由は、返されたものNSStringをどこかに保持していて、それを解放するのを忘れているためです。つまり、リークされたメモリは使用しているライブラリ内に割り当てられますが、解放されない理由はライブラリの外部にあり、ライブラリ自体がリークの責任を負うことはできません。

メソッドencryptStringは、エンコードされた文字列を取得して呼び出し元に渡すように見えます。参照カウントはどこにも変更されないため、呼び出し元の場所を調べて、encryptString誤って文字列を保持している場所がないかどうかを確認する必要があります。後でリリースしないでください。

于 2012-06-05T12:47:22.203 に答える