Base64EncodedString内存泄漏

时间:2012-06-05 12:38:17

标签: objective-c memory-leaks base64

我正在githubt找到一个NSData + Base64 opensrc库,用于下个月到期的项目。我刚刚开始分析,分析和优化,并发现了来自该代码的泄漏。我已禁用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)任何人都知道如何解决它,或者我可以用另一个库替换它?

谢谢!

编辑: 我更改了以上几行,仪器精确定位为泄漏代码:

 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,因此它没有被释放,这就是我收到警告的原因。有趣的是,在我意识到错误之前,这是我看过的最后一段代码。谢谢你的帮助

1 个答案:

答案 0 :(得分:1)

您的分析可能正确,泄漏来自重新分配outputBytes的地方。但请注意,泄漏的“真正”罪魁祸首是outputBytes最终没有被释放。问题是为什么它没有被释放。

如果您查看下一行,outputBytes直接输入NSString而不复制它指向的字符串的内容,所以我想作者的意图是{从那时起,{1}}将获得NSString内存块的所有权,outputBytes负责释放它。当NSString的引用计数器变为零时,NSString将释放该块。代码中的下几行显示NSString变为自动释放:

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

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

您的encryptString方法似乎采用编码字符串并将其传递给调用者,而不是在任何地方更改其引用计数,因此您必须继续查看要调用{{1}}的地方看看是否有任何地方你不小心保留了绳子而后来没有松开绳子。