UIApplication Delegate 弱引用导致它为零?

Posted

技术标签:

【中文标题】UIApplication Delegate 弱引用导致它为零?【英文标题】:UIApplication Delegate weak reference causing it to be nil? 【发布时间】:2014-07-29 23:04:20 【问题描述】:

当一个 UIApplicationDelegate 被实例化时:

UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))

即使[[UIApplication sharedApplication] delegate] 属性是弱引用,AppDelegate 仍保留在内存中。

但是,如果您取消设置并重新设置委托属性,如下所示:

id originalDelegate = [[UIApplication sharedApplication] delegate];
[[UIApplication sharedApplication] setDelegate:nil];
[[UIApplication sharedApplication] setDelegate:originalDelegate];

然后它变得无效使用。知道为什么它最初可以使用弱引用吗?

【问题讨论】:

你是如何使用它的? 制作第三方库。我需要临时将另一个 UIApplication 的委托更改为我自己的(以在传递其他方法时覆盖某些方法),或者调换一些方法,这需要取消设置/重置,因为 UIApplication 会一次性检查委托方法是否存在环境。比任何事情都更令人好奇的是,如果有更好的方法来保持原始应用程序委托的强大,而不是在应用程序生命周期的剩余时间里保持一个静态变量。 @DavidLiu 所以如果调用setDelegate: 使缓存无效,你可以直接调用它而不首先将代理设置为nil,还是检查它是否是同一个实例? @DavidLiu 您是否有可能通过注册应用程序对象发布的许多通知中的一些来完成您需要的事情,而不是直接收到消息? @jlehr 不幸的是,没有。被覆盖的委托方法有一个需要更改的返回值。 【参考方案1】:

当指向它的指针设置为 nil 时,它与应用程序的窗口一起被释放。保持指向它的强指针,它会一直存在......

@property(strong, nonatomic) AppDelegate *strongAppDelegate;

self.strongAppDelegate = [[UIApplication sharedApplication] delegate];
[[UIApplication sharedApplication] setDelegate:nil];
[[UIApplication sharedApplication] setDelegate:self.strongAppDelegate];

这可以通过在您的 AppDelegate 和 NSLogging 中实现 dealloc 来证明。

编辑 - 无论如何,阅读你打算如何使用它,考虑两个替代方案:

    将方法添加到要从客户端的应用程序委托调用的 API 挂钩 (更好)订阅 UIApplication 的状态变化 通过 NSNotificationCenter(例如“通知”下的see the doc。它发布类似 UIApplicationDidBecomeActiveNotification)。

在客户的应用程序和委托钩子之间插入(即使只是片刻)似乎是大多数人不喜欢的妥协。

【讨论】:

我想我想知道的问题是为什么它最初很好。如果 UIApplication 只保留对委托的弱引用,那么一开始是什么让它保持活动状态?我意识到我可以用一个长时间运行的静态变量来保留它,但作为一个第三方库,这似乎有点粗略。 我认为正在发生的事情是它被分配了 UIApplicationMain,所以当分配给 sharedApplication 时它是 +1 保留计数。弱 ref 不会增加保留计数,但设置为 nil 遵循常规规则。它被释放了,然后它就永远消失了。 @danh 不需要这个答案。在 OP 的代码中,originalDelegate 是一个强引用。 @rmaddy 是什么让你这么想?在我看来,它就像一个局部变量。 @danh 是的,它的局部和局部变量默认为strong【参考方案2】:

当然,实现是不透明的,并且将来可能会发生变化,但似乎框架在启动时对retain进行了额外的调用,然后通过在@987654323中发送release来平衡它@。您可能必须将原始委托实例存储在某处的属性中。我想如果你想特别注意潜在的泄漏,你可以尝试使用 KVO 来观察应用程序对象的 delegate 属性的变化,这样你就可以对额外的属性进行相应的更改。

不过,这可能还是有点冒险,因为我们不知道幕后还有什么可能受到影响。 (例如,应用程序委托可能需要注册才能接收某些通知等。)这很不幸,因为在运行时切换应用程序委托实例在我看来并不是一件不合理的事情。

编辑

如果可能的话,看看你是否可以通过注册UIApplication 实例发布的一些通知来完成你需要的事情——它们非常全面。不幸的是,您一直在探索的方法非常冒险。

【讨论】:

是的,总的来说,这一切都非常冒险。 KVO 监视 UIApplication 委托属性的想法似乎非常有用,尽管它可以处理任何潜在的泄漏(这是我对该方法最大的担忧)。【参考方案3】:
id originalDelegate = [[UIApplication sharedApplication] delegate]; //weak reference point to delegate. delegate is a weak property
[[UIApplication sharedApplication] setDelegate:nil]; // delegate point to nil, originalDelegate was pointing to delegate so now originalDelegate points to nil as well.
[[UIApplication sharedApplication] setDelegate:originalDelegate]; // delegate points to originalDelegate which was pointing already to nil.

【讨论】:

OP 在编译时启用了 ARC。局部变量的默认生存期限定符是 __strong,因此 originalDelegate 在局部范围结束之前仍具有至少 1 的保留计数。 确实如此,您的 [UIApplication sharedApplication] 有一个委托属性,当您使用要设置的设置器时,可以作为 [[UIApplication sharedApplication] delegate] 或 [UIApplication sharedApplication].delegate 访问iVar 通过访问器访问 nil,而 originalDelegate 已经指向名为 delegate 的属性。不超过允许您使用点运算符的访问器 当您调用 [[UIApplication sharedApplication] setDelegate:nil] 时,您将 UIApplication 的委托引用更改为指向 nil,您并没有将委托本身设置为 nil,我认为您的困惑就在那里。 但是 UIapplication 有一个名为 delegate 的属性。当您调用 setDelegate 时,您正在使用看起来非常简单的访问器,例如。查看有关 ios 中属性的文档并合成 - (void)setDelegate:(id)something _delegate = something 您现在应该知道,属性只不过是使用点运算符访问 iVar 的能力。 [UIApplication sharedApplication].delegate = something 等价于 [[UIApplication sharedApplication] setDelegate:something]

以上是关于UIApplication Delegate 弱引用导致它为零?的主要内容,如果未能解决你的问题,请参考以下文章

[UIApplication sharedApplication].delegate 的铸造

UIApplication和delegate

我应该啥时候释放 [[UIApplication sharedApplication] delegate] 对象?

Swift/iOS13 - UIApplication.shared.delegate.window?.rootViewController?.present() 来自自定义类

iOS [[UIApplication sharedApplication] delegate]运用

UIApplication Delegate 弱引用导致它为零?