【问题标题】:Objective C Blocks as Async-callbacks & BAD ACCESSObjective C 块作为异步回调和不良访问
【发布时间】:2011-10-16 15:55:29
【问题描述】:

我有一个严重的疑问。假设如下场景:

  1. 您的屏幕上有一个UIViewController
  2. 应用程序使用块作为回调启动后端调用
  3. 您使用“自我”代理来防止保留循环。
  4. 用户点击“返回”,UIViewController 被解除锁定。
  5. 迟早会执行回调块 >> BAD ACCESS

在 iOS 4 之前,我们通过将...的 delegate 属性设置为 nil 来处理这种情况。我不知道,无论您使用的是什么类。

但是现在......你如何取消一个块??如果块被发送到静态方法,而您无法清除该回调引用怎么办??。

在这种情况下,我们是否应该避免使用“self”代理项?

顺便说一句,“自我”代理,我的意思是:

__block typeof(self) bself = self;

谢谢!!

【问题讨论】:

  • 一旦视图被解除分配,如何将静态块变量设置为 NULL?
  • 你的意思是,拥有一个静态的 __block 变量(或一个类成员),然后将其清除?
  • 我希望找到某种 API 来命名和杀死一个块。但我没有意识到我可以只拥有一个 __block 作为班级成员。这太简单了......谢谢亚历克斯!
  • @AlexNichol,您能详细说明一下将此块变量设置为 NULL 吗? __block 变量在哪里声明以及如何声明?谢谢
  • @bandejapaisa 的想法是维护一个静态块变量,并在对象被解除分配后将其设置为 NIL。

标签: objective-c ios objective-c-blocks


【解决方案1】:

好吧,首先: 如果(且仅当)您避免使用 self 或直接访问块内的 ivars 的原因确实是保留循环,那么您应该处于类似的情况

client => objectA => blockWithWeakBackReference

(其中=> 表示“强烈引用”)。

在这种情况下,blockWithWeakBackReference 只能由objectA 调用,因此不会出现访问错误的危险。

如果我正确理解您的问题,那么您的真正意思是另一种情况:

  • objectA 希望某个应用程序范围的服务代表它执行一个块,前提是满足某些先决条件。
  • 您避免在块内使用self,因为您希望能够在块执行之前处置objectA

这方面的一个示例可能是共享网络队列,它在请求因某种原因完成加载时执行一个块。

在这种情况下,我建议简单地复制NSNotificationCenteraddObserverForName:object:queue:usingBlock: 的设计并让您的服务实现一对方法,例如-(SomeTokenObjectType)addWorkerBlock:(void(^)(whatever-signature-makes-sense-for-you))-(void)cancelWorkerBlockWithToken:(SomeTokenObjectType),以便排队并取消您的回调-块。

然后,使用此服务的所有对象都可以简单地使用 NSMutableSet 类型的 ivar 来存储每个排队块的令牌,并在其 dealloc 中枚举剩余的令牌,并使用服务取消它们。

【讨论】:

  • 这正是我与丹妮打交道的场景。 非常感谢您的 cmets,我真的很感激。我要实现这个机制,我真的很喜欢它的外观!。
  • 已经有一段时间了,但我想为遇到此问题的任何人添加一些有趣的信息以解决相关问题:在他令人敬畏的文章 Let's build NSNotificationCenter 中,Mike Ash 以一种非常干净和优雅的方式实现了这个机制——所以成为一个伟大的艺术家并借鉴他的方法;-)
【解决方案2】:

“防止保留循环。”

但是您真的有要防止的保留循环吗?想想这个。该块保留self(您的视图控制器)。后端调用保留该块。但是self 保留块在哪里呢?

【讨论】:

  • 如果在(3)中不使用自代理,当前的 UIViewController 将被保留,直到您得到后端的响应。
  • 这一点都不酷。如果用户点击“返回”,当前的 viewController 应该被释放。 “保留周期”部分取决于您的通信层……理论上,您是对的。如果代码写得好,你不应该得到一个保留周期。然而,您将使用比实际需要更多的资源。
  • @JorgeLeandroPerez:是的,它会一直保留到后端任务完成。但是来吧,这没什么大不了的。您知道视图控制器在弹出时将被释放是不正确的。由于各种原因,操作系统中的其他内容可能仍会保留。
  • 无论如何,除非有办法取消后端任务,否则后端任务必须保留它回调的东西。所以你必须要么保留这个 UIViewController,或者,如果你对这个视图控制器的内存使用非常严格,你可以让块改为保留一个(小)虚拟代理对象,你的视图控制器可以是这个虚拟的委托对象,并且它在解除分配时会将委托为零。
  • 这是一个很老的问题。如今,我一直在做的,就是你所说的。除非很少有特殊情况,否则内存使用量很大。在这些情况下,我只存储活动的 requestId,并在返回之前取消它们。感谢您的帮助!
猜你喜欢
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多