【问题标题】:Why objects are not dealloced in the dealloc method?为什么在 dealloc 方法中没有释放对象?
【发布时间】:2015-05-29 17:17:30
【问题描述】:

我无法理解 Objective-C 和 ARC。

据我了解,强指针会自动为您解除分配,因此您不必考虑它(在 dealloc 方法中解除分配,还是在上次使用对象之后?)。

所以我写了一个小应用程序,有 2 个 viewController 和一个 NavigationController,它进入一个视图然后返回。

调用了 dealloc 方法,但是我在 viewDidLoad 方法中设置的属性没有被释放,它仍然指向某个对象。

代码: 第一个 viewController 有一个按钮,通过按下它,执行到另一个 viewController 的 segue。那里没有代码。

SecondViewController.m

@interface SecondViewController () 
@property (nonatomic, strong) NSString *myString;
@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"%@", _myString);
    _myString = @"it works";
}

- (void)dealloc {
    NSLog(@"%@", _myString);
    // and now it is deallocating the _myString property ???
}

@end

然后,我尝试做另一件事。

这个想法是创建一个弱指针,它指向与强指针相同的内存地址。不过,无论如何,弱指针都应该为零。

  1. 由于调用了dealloc方法,所有的弱指针都应该被niled

  2. 由于强指针只在viewDidLoad中使用,它应该在dealloc方法之前被释放。

问题是,它没有被释放。 为什么?

secondViewController 的代码:

@interface SecondViewController ()
@property (nonatomic, strong) NSString *myString;
@property (nonatomic, weak) NSString *test;
@end

@implementation SecondViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"%@", _myString);
    _myString = @"it works";
    _test = _myString;
}

- (void)dealloc
{
    NSLog(@"%@", _test);
}

@end

【问题讨论】:

  • 请注意,像这样的测试行为永远不会起作用。首先,像@"it works" 这样的静态字符串永远不会被释放,因为它从一开始就没有被分配;它是可执行映像的一部分。其次,即使对象被解除分配,向解除分配的对象发送消息也会表现出未定义的行为,并且不能保证会导致崩溃或其他症状。
  • 什么意思,它从来没有分配过?那么它是像应用程序生命周期的“全局”变量还是......? _myString = @"it works" 与 _myString = [[[NSString alloc] initWithString:@"it works"] autorelease] 或类似的东西不一样吗?因为每当我尝试写这样的东西时,Xcode 都会向我显示写 _myString = @"it works" 的警告
  • @denis631 是的。但这与我在答案中使用stringWithFormat: 构建字符串的方式不同。
  • @denis631 由@"..." 创建的字符串实际上是在编译时创建的,是应用程序可执行文件的一部分。它与应用程序的其余数据(和代码)一起映射到内存中,因此,它不是应用程序分配堆的一部分。所述对象在释放或保留时不做任何事情,并且它们永远不会被释放,因为首先没有分配。使用 NSString(或其他框架类)经常会因为这个和其他(参见标记的指针)原因而感到困惑。在进行此类学习练习时,请始终使用您自己的 NSObject 子类!

标签: ios objective-c memory-management memory-leaks dealloc


【解决方案1】:

属性的释放发生在 dealloc 方法的末尾。如果你覆盖了 dealloc 方法,属性还不会在该方法中被释放。


您可以通过在第一个视图控制器中创建一个弱属性来测试这一点,分配第二个视图控制器的强属性,然后在应用程序返回到第一个视图控制器时记录它的值。

【讨论】:

  • 对于自动释放的对象,它也可能发生在 dealloc 结束之后,此时自动释放池耗尽。这通常发生在运行循环的每次迭代中。
【解决方案2】:

说明weak 引用的最简单方法是使用以下示例...

给定以下两个类:

@interface ObjectWithStrongRef : NSObject

@property (strong) NSString *ref;

@end

@interface ObjectWithWeakRef : NSObject

@property (weak) NSString *ref;

@end

我们将创建一个ObjectWithWeakRef的实例,其作用域大于ObjectWithStrongRef,为后者的ref属性赋值,然后让前者的ref指向同一个对象,然后我们将检查ref 在两个范围内。

int main(int argc, const char * argv[]) {
    ObjectWithWeakRef *weak = [[ObjectWithWeakRef alloc] init];

    @autoreleasepool {
        ObjectWithStrongRef *strong = [[ObjectWithStrongRef alloc] init];
        strong.ref = [NSString stringWithFormat:@"Hello %@", @"World"];
        weak.ref = strong.ref;

        NSLog(@"Weak.Ref = %@", weak.ref);
    }

    NSLog(@"Weak.Ref = %@", weak.ref);
}

请注意,我们不能简单地将ref 分配给文字字符串。 Objective-C 倾向于将这些保留在内存中,因此它可以进行一些内存优化,但是当我们使用 stringWithFormat: 时,它会创建一个自动释放字符串。

在第一个NSLog 语句中,strong.ref 保持对字符串对象的强引用,因此当我们记录weak.ref 时,该对象尚未释放,因此它正确记录了“Hello World”。

在第一次和第二次NSLog 调用之间,我们退出了@autoreleasepool,其中strong 对象的作用域(如果我们将NSLog 消息放在ObjectWithStrongRefdealloc 中,我们会在这里看到它)。因为strong 在我们退出@autoreleasepool 时已解除分配,所以不再有对我们引用的字符串对象的任何强引用——我们只有weak 对内存的弱引用,所以字符串对象也解除分配(就在strong被解除分配之后)。

所以在第二个NSLog 调用中,我们将看到Weak.Ref = (null) 打印出来。

【讨论】:

  • 请注意,依赖于弱引用无效的确切时间是非常脆弱的。唯一可以保证的是,弱引用将在被引用的对象被销毁之前清零。破坏发生的时间可能会因微妙的原因而有所不同。例如,将strong 的声明(但不是赋值)移到@autoreleasepool 块之外,输出将非常不同。
  • @bbum 对。我不建议依赖 weak 引用归零的时间,但您永远不应该收到异常发送消息到 weak 引用,对吗?要么对象仍然存在(即使它即将解除分配),要么weak 被清零,向它发送消息将不做任何事情并返回nil
猜你喜欢
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
相关资源
最近更新 更多