0

我正在将 githubt 上的 NSData+Base64 opensrc 库用于下个月到期的项目。我刚刚开始分析、分析和优化,并发现了来自该代码的泄漏。我已经禁用了 NSZombie 和所有调试方法和 try-catch,因为我之前发现它们有时会泄漏内存。精确定位泄漏的确切线是:

- (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);

但是,我仍然从那条线上泄漏。目标 C 中的 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而不复制它指向的字符串的内容,所以我猜作者的意图是从那时起NSString获得内存块的所有权,它是释放它outputBytes的责任。当它的引用计数器变为零时,将释放这个块。代码中接下来的几行显示自动释放:NSStringNSStringNSString

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

如果NSString是自动释放的,那么一切都应该没问题——所有权由自动释放池获取,使用它时您NSString不必自己拥有它。release上面的代码片段还说,autorelease只有在没有 ARC(自动引用计数)的情况下编译代码时才会调用它。所以,我认为代码是正确的,因此outputBytes可能泄露的唯一原因是因为您将返回的内容保留在NSString某处,然后忘记释放它。换句话说,泄漏的内存是在您正在使用的库中分配的,但它没有被释放的原因在于库之外,并且库本身不能对泄漏负责。

您的encryptString方法似乎采用编码字符串并将其传递给其调用者,而不是在任何地方更改其引用计数,因此您必须继续查看您正在调用的地方,encryptString看看是否有任何地方您不小心保留了字符串并且以后不发布。

于 2012-06-05T12:47:22.203 回答