【问题标题】:Why there is no retain loop between UINavigationController and UIViewControllers为什么 UINavigationController 和 UIViewControllers 之间没有保留循环
【发布时间】:2014-02-22 20:32:15
【问题描述】:

情况:有 UINavigationController 和推送的 UIViewController。

1.UIViewController对UINavigationController有强引用

@property(nonatomic,readonly,retain) UINavigationController *navigationController

2.UINavigationController 将视图控制器存储在 NSArray 中

@property(nonatomic,copy) NSArray *viewControllers;

UINavigationController 应该对此 NSArray 有强引用(否则它将被释放)。

3.NSArray 对包含的视图控制器有很强的引用。

更新: 让我们想象一下我们在代码中的某个地方:

UIViewController *A = [ [UIViewController alloc] init ];
UINavigationController *B = [ [ UINavigationController alloc ] initWithRootViewController:A ];
// Here we have strong reference in A to B, in B to B.viewControllers (count == 1) and in B.viewControllers to A.
// Local variable's strong references to A and B
A = nil; B = nil;
// Local variable's strong references has gone
// But we should still have retain loop here
// Magic !?? :)

我的问题是为什么我们这里没有保留循环?

【问题讨论】:

  • 您的第 3 点仅适用于堆栈中的视图控制器。当 ViewController 被移除(dismissed)时,它不再是真的。
  • 请看我的示例(更新下方)视图控制器在哪里弹出?
  • @JoshCaswell 但是是什么破坏了它?我知道如果孩子从堆栈中弹出,它是如何被破坏的,但是这里发生的一切是我们停止引用(保留)导航控制器及其根视图控制器。如果他们真的互相保留,那就是一个保留循环,任何一方都无法收到dealloc(保留计数不能达到零)。
  • 最简单的解释是 docs / public 标头中的错误。 (你可以基于这个理由提交一个错误。)但我们永远不会知道,因为我们看不到 Apple 的代码。可以说事实上没有保留循环,因为导航控制器和它的根视图控制器确实事实上一起消失了。这就是所有需要知道的。
  • @JoshCaswell 对,内存管理属性只对readwrite 属性有意义,即使那样,也只对synthesized 属性有意义;即使有一个 setter,它也不会告诉你一个非综合的 setter 到底做了什么。

标签: ios objective-c cocoa-touch retain-cycle


【解决方案1】:

2.UINavigationController 将视图控制器存储在NSArray

这不是给定的。

@property(nonatomic,copy) NSArray *viewControllers;

这绝不表示存在名为_viewControllers 或类似名称的ivar。它只是告诉我们有一些方法-viewControllers 会返回一个NSArray,并且有一些方法setViewControllers: 会接受一个,并暗示它会复制它(或至少表现就像它复制了它一样)。这就是它告诉我们的全部。如果您在调试器中展开 NSNavigationController,您会注意到那里没有列出 _viewControllers ivar。

如果你仔细观察,你会发现-viewControllers 没有作为综合属性实现。它只是转发到-childViewControllers(这是一个UIViewController 属性)。好的,那不只是解决问题吗?我的意思是-childViewControllers 实现为[NSArray arrayWithArray:_childViewControllers]。很公平。你抓住了我。

但同样的逻辑也适用于[UIViewController navigationController]。本声明:

@property(nonatomic,readonly,retain) UINavigationController *navigationController

并不意味着它实际上有一个强链接。这只是意味着如果你打电话给setNavigationController:,你会期望它保留它。但是你不能打电话给setNavigationController:。没有这样的方法(甚至没有私人方法)。所以这一切真正有希望的是有一个方法叫做-navigationController被实现为对+[UINavigationController _ancestorViewControllerOfClass:allowModalParent:] 的调用。这只是传递给UIViewController 实现,它沿着parentViewController 链向上寻找UINavigationController。所以没有保留循环;它是动态确定的。

但你的问题仍然是一个好问题。这里的头文件使 IMO 感到困惑,我会针对它打开一个雷达。 navigationController 应该被列为 assign 或者它应该什么都不说(即使默认为 strong 它至少不会误导)。

顺便说一句,如果你对这些东西感兴趣,你真的应该花 90 美元购买 Hopper。它非常擅长这种探索。

【讨论】:

  • 感谢您的回答和 Hopper 链接。
  • 感谢 Hopper 链接 Rob。如果您在某处有一篇关于您使用的电子工具的文章 - 您会分享吗?总的来说,了解大师的工具非常非常有趣(这里没有接吻:))。干杯!
  • 我所做的逆向工程工作中有 90% 是 Hopper、nm、otool 和字符串(以及 Mac 上的 F 脚本,尽管最近对我没有用)。我过去经常使用 class-dump,但我一直无法找到适用于现代应用程序的良好替代品。但我并没有很认真地做逆向工程;例如,我几乎从不尝试打开 App Store 应用(任何认真的探索者都会经常这样做)。
【解决方案2】:

UINavigationControllerviewControllers 属性定义为

@property(nonatomic, copy) NSArray *viewControllers

当前在导航堆栈上的视图控制器。

那么没有问题的保留循环,因为当视图控制器被解除时,导航控制器会从该数组中删除任何UIViewController

当您没有任何机制通过在某些时候释放保留的对象来打开循环时(如果非 ARC,则使用 release,或将强属性设置为 nil),保留循环是一个问题。

【讨论】:

  • 我的示例中视图控制器在哪里被解除?没有指令B.viewControllers = [NSArray ...] 没有[B popViewController] 没有[B popToRootViewController]。它在哪里被解雇?
  • 问题不在于解雇。问题是,如果 A 保留 B,B 保留 A,那么 当没有其他对它们的引用时,A 和 B 怎么可能不存在
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多