ARC 已启用但存在内存泄漏 (Objective C)
ARC is enabled but having Memory Leak (Objective C)
如您所见,下面的代码除了枚举一组文件外并没有做太多(全部注释掉),但是,在 运行 40 秒后,我的内存使用量增长到超过 2 GB通过按 UI.
上的按钮启动下面的功能
我可以运行UI几个小时,在按下按钮之前,内存使用不超过8MB。
鉴于 ARC 已打开,内存中保存的是什么?
removed original code as the edit below made no differance.
编辑:
尝试 @autoreleasepool{ dispatch_asyny ... }
并在 while 循环周围和 while 循环内进行排列,但没有效果。
这是添加并清理了 autorelasepool 的代码
-(void) search{
self.dict = [[NSMutableDictionary alloc] init];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/SeaWall.log"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *bundleRoot = @"/";
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot];
NSString *filename;
while ((filename = [NSString stringWithFormat:@"/%@", [direnum nextObject]] ) && !self.exit) {
@autoreleasepool {
NSString *ext = filename.pathExtension;
if ([ext hasSuffix:@"so"] || [ext hasSuffix:@"dylib"] ) {
if (filename == nil || [NSURL URLWithString:filename] == nil) {
continue;
}
NSData *nsData = [NSData dataWithContentsOfFile:filename];
if (nsData != nil){
NSString *str = [nsData MD5];
nsData = nil;
[self writeToLogFile:[NSString stringWithFormat:@"%@ - %@", [filename lastPathComponent], str]];
}
}
ext = nil;
} // end autoreleasepool
}
[fileHandle closeFile];
[self ControlButtonAction:nil];
});
}
内存并没有完全泄漏:它非常适合释放,但从来没有机会释放。
ARC 建立在 Objective-C 的手动内存管理规则之上。基本规则是“调用 init
的 object/function 拥有新实例”,所有者必须 release
对象不再需要它时。
这是创建对象的便捷方法的问题,例如 [NSData dataWithContentsOfFile:]
。该规则意味着 NSData
class 拥有该实例,因为它调用了 init
。一旦返回值,class 将不再需要该对象,并且需要释放它。但是,如果这发生在被调用者有机会保留实例之前,它就会在任何事情有机会发生之前消失。
为了解决这个问题,Cocoa引入了autorelease
方法。此方法将对象的所有权转移到最后设置的自动释放池。当您退出其范围时,自动释放池将被“耗尽”。
Cocoa/AppKit/UIKit 会围绕事件处理程序自动设置自动释放池,因此您通常不需要为此担心。然而,如果你有一个 long-运行 方法,这就会成为一个问题。
您可以使用 @autoreleasepool
语句声明一个自动释放池:
@autoreleasepool
{
// code here
}
在右括号处,自动释放池收集的对象被释放(如果没有其他人引用它们,则可能被释放)。
因此您需要在此语句中包装循环体。
这是一个例子。此代码在我的计算机上每秒“泄漏”大约 10 兆字节,因为执行永远不会离开 @autoreleasepool
范围:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
while (true)
{
NSString* path = [NSString stringWithFormat:@"%s", argv[0]];
[NSData dataWithContentsOfFile:path];
}
}
}
另一方面,内存使用保持稳定,因为执行在每次循环迭代结束时离开 @autoreleasepool
范围:
int main(int argc, const char * argv[])
{
while (true)
{
@autoreleasepool
{
NSString* path = [NSString stringWithFormat:@"%s", argv[0]];
[NSData dataWithContentsOfFile:path];
}
}
}
在循环条件中创建对象对于长循环来说很尴尬,因为它们不会被内部 @autoreleasepool
拾取。您还需要将它们放在 @autoreleasepool
范围内。
返回
每当我们return
一个对象(可能是Swift
),我们需要注册到最近的@autoreleasepool
块(通过调用autorelease
方法来防止内存泄漏,根据 ownership-rules),但现在 ARC
会自动为我们完成;
每当 ARC
禁用时;使用 alloc
and/or init
后,手动调用 autorelease
,例如:
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName] autorelease];
return string;
}
内存需要由自动释放池释放。
否则它会像你正在经历的那样被锁住并且它会泄漏。
在你的循环中输入:
@autoreleasepool { /* BODY */ }
如您所见,下面的代码除了枚举一组文件外并没有做太多(全部注释掉),但是,在 运行 40 秒后,我的内存使用量增长到超过 2 GB通过按 UI.
上的按钮启动下面的功能我可以运行UI几个小时,在按下按钮之前,内存使用不超过8MB。
鉴于 ARC 已打开,内存中保存的是什么?
removed original code as the edit below made no differance.
编辑:
尝试 @autoreleasepool{ dispatch_asyny ... }
并在 while 循环周围和 while 循环内进行排列,但没有效果。
这是添加并清理了 autorelasepool 的代码
-(void) search{
self.dict = [[NSMutableDictionary alloc] init];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/SeaWall.log"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *bundleRoot = @"/";
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot];
NSString *filename;
while ((filename = [NSString stringWithFormat:@"/%@", [direnum nextObject]] ) && !self.exit) {
@autoreleasepool {
NSString *ext = filename.pathExtension;
if ([ext hasSuffix:@"so"] || [ext hasSuffix:@"dylib"] ) {
if (filename == nil || [NSURL URLWithString:filename] == nil) {
continue;
}
NSData *nsData = [NSData dataWithContentsOfFile:filename];
if (nsData != nil){
NSString *str = [nsData MD5];
nsData = nil;
[self writeToLogFile:[NSString stringWithFormat:@"%@ - %@", [filename lastPathComponent], str]];
}
}
ext = nil;
} // end autoreleasepool
}
[fileHandle closeFile];
[self ControlButtonAction:nil];
});
}
内存并没有完全泄漏:它非常适合释放,但从来没有机会释放。
ARC 建立在 Objective-C 的手动内存管理规则之上。基本规则是“调用 init
的 object/function 拥有新实例”,所有者必须 release
对象不再需要它时。
这是创建对象的便捷方法的问题,例如 [NSData dataWithContentsOfFile:]
。该规则意味着 NSData
class 拥有该实例,因为它调用了 init
。一旦返回值,class 将不再需要该对象,并且需要释放它。但是,如果这发生在被调用者有机会保留实例之前,它就会在任何事情有机会发生之前消失。
为了解决这个问题,Cocoa引入了autorelease
方法。此方法将对象的所有权转移到最后设置的自动释放池。当您退出其范围时,自动释放池将被“耗尽”。
Cocoa/AppKit/UIKit 会围绕事件处理程序自动设置自动释放池,因此您通常不需要为此担心。然而,如果你有一个 long-运行 方法,这就会成为一个问题。
您可以使用 @autoreleasepool
语句声明一个自动释放池:
@autoreleasepool
{
// code here
}
在右括号处,自动释放池收集的对象被释放(如果没有其他人引用它们,则可能被释放)。
因此您需要在此语句中包装循环体。
这是一个例子。此代码在我的计算机上每秒“泄漏”大约 10 兆字节,因为执行永远不会离开 @autoreleasepool
范围:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
while (true)
{
NSString* path = [NSString stringWithFormat:@"%s", argv[0]];
[NSData dataWithContentsOfFile:path];
}
}
}
另一方面,内存使用保持稳定,因为执行在每次循环迭代结束时离开 @autoreleasepool
范围:
int main(int argc, const char * argv[])
{
while (true)
{
@autoreleasepool
{
NSString* path = [NSString stringWithFormat:@"%s", argv[0]];
[NSData dataWithContentsOfFile:path];
}
}
}
在循环条件中创建对象对于长循环来说很尴尬,因为它们不会被内部 @autoreleasepool
拾取。您还需要将它们放在 @autoreleasepool
范围内。
返回
每当我们return
一个对象(可能是Swift
),我们需要注册到最近的@autoreleasepool
块(通过调用autorelease
方法来防止内存泄漏,根据 ownership-rules),但现在 ARC
会自动为我们完成;
每当 ARC
禁用时;使用 alloc
and/or init
后,手动调用 autorelease
,例如:
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName] autorelease];
return string;
}
内存需要由自动释放池释放。 否则它会像你正在经历的那样被锁住并且它会泄漏。
在你的循环中输入:
@autoreleasepool { /* BODY */ }