ios – 为什么UIViewController在主线程上解除分配?

ios – 为什么UIViewController在主线程上解除分配?,第1张

概述我最近在一些Objective-C代码中偶然发现了 The Deallocation Problem.这个主题之前在 Block_release deallocating UI objects on a background thread的Stack Overflow上讨论过.我想我理解这个问题及其含义,但我确定我想在一个小测试项目中重现它.我首先创建了自己的SOUnsafeObject(=一个应 我最近在一些Objective-C代码中偶然发现了 The Deallocation Problem.这个主题之前在 Block_release deallocating UI objects on a background thread的Stack Overflow上讨论过.我想我理解这个问题及其含义,但我确定我想在一个小测试项目中重现它.我首先创建了自己的SOUnsafeObject(=一个应该总是在主线程上释放的对象).

@interface SOUnsafeObject : NSObject@property (strong) Nsstring *Title;- (voID)reloadDataInBackground;@end@implementation SOUnsafeObject- (voID)reloadDataInBackground {    dispatch_async(dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_DEFAulT,0),^{        dispatch_async(dispatch_get_main_queue(),^{            self.Title = @"RetrIEved data";        });        sleep(3);    });}- (voID)dealloc {    NSAssert([NSThread isMainThread],@"Object should always be deallocated on the main thread");}@end}]

现在,正如预期的那样,如果我把[[[SOUnsafeObject alloc] init] reloadDataInBackground];内部应用程序:dIDFinishLaunching ..由于断言失败,应用程序在3秒后崩溃.拟议的修复似乎有效.即如果我将reloadDataInBackground的实现更改为:应用程序不再崩溃:

__block SOUnsafeObject *safeSelf = self;dispatch_async(dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_DEFAulT,^{    dispatch_async(dispatch_get_main_queue(),^{        safeSelf.Title = @"RetrIEved data";        safeSelf = nil;    });    sleep(3);});

好的,所以看起来我对这个问题的理解以及如何在ARC下解决它是正确的.但只是100%肯定..让我们尝试使用UIVIEwController(因为UIVIEwController可能在现实生活中填写SOUnsafeObject的角色).该实现几乎与SOUnsafeObject的实现相同:

@interface SODemoVIEwController : UIVIEwController- (voID)reloadDataInBackground;@end@implementation SODemoVIEwController- (voID)reloadDataInBackground {    dispatch_async(dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_DEFAulT,@"UI objects should always be deallocated on the main thread");    NSLog(@"I'm deallocated!");}@end

现在,让我们把[[SODemoVIEwController alloc] init] reloadDataInBackground];内部应用程序:dIDFinishLaunching …嗯,断言没有失败..消息我已经解除分配! 3秒后打印到控制台,所以我很确定视图控制器已被取消分配.

为什么在后台线程上释放不安全对象时,视图控制器在主线程上取消分配?代码几乎相同. UIKit是否在幕后做了一些奇特的事情以确保UIVIEwController始终在主线程上被释放?我开始怀疑这一点,因为下面的代码片段也没有打破我的断言:

dispatch_async(dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_DEFAulT,{    SODemoVIEwController()});

如果是这样,这种行为是否记录在某处?可以依赖这种行为吗?或者我完全错了,有什么显而易见的我在这里缺席了吗?

注意:我完全清楚我可以在这里使用__weak引用这一事实,但我们假设视图控制器应该仍处于活动状态,以便在主线程上执行完成代码.此外,在我绕过它之前,我试图了解问题的核心.我将代码转换为Swift并得到与Objective-C相同的结果(SOUnsafeObject的修复在语法上甚至更加丑陋).

解决方法 tl; dr – 虽然我找不到官方文档,但当前的实现确实确保UIVIEwController的dealloc发生在主线程上.

我想我可以给出一个简单的答案,但也许我今天可以做一点“教人钓鱼”.

好.我无法在任何地方找到这方面的文档,我也不记得曾经公开说过.事实上,我总是不遗余力地确保视图控制器在主线程上被释放,这是我第一次看到有人表示UIVIEwController对象在主线程上自动解除分配.

也许别人可以找到官方声明,但我找不到.

但是,我确实有一些证据证明它确实发生了.实际上,起初,我认为你没有正确处理你的块或引用计数,并且不知何故在主线程上保留了引用.

然而,经过粗略的看,我有兴趣为自己尝试.为了满足我的好奇心,我创建了一个类似于你的类,它继承自UIVIEwController.它的dealloc运行在主线程上.

所以,我只是将基类更改为UIResponder,这是UIVIEwController的基类,并再次运行它.这次它的dealloc运行在后台线程上.

嗯.也许闭门造车会发生一些事情.我们有很多调试技巧.答案总是在你尝试的最后一个答案,但我想我会尝试我常用的这类技巧.

日志通知

我最喜欢的工具之一就是记录所有通知.

[[NSNotificationCenter defaultCenter]    addobserverForname:nil                object:nil                 queue:nil            usingBlock:^(NSNotification *note) { NSLog(@"%@",note); }];

然后我运行了两个类,并没有看到两者之间的任何意外或不同.我没想到,但这个小技巧很简单,它帮助我发现了很多其他的东西是如何工作的,所以它通常是第一个.

记录方法/消息发送

我的第二个技巧是启用方法记录.但是,我不想记录所有方法,只是在最后一个块执行的时间和对dealloc的调用之间发生的事情.因此,通过将其添加为“休眠”块的最后一行来打开方法日志记录.

instrumentObjcmessageSends(YES);

然后我将记录恢复,这是dealloc方法的第一行.

instrumentObjcmessageSends(NO);

现在,在我知道的任何头文件中都找不到这个C函数,所以你需要在文件的顶部声明它.

extern voID instrumentObjcmessageSends(BOol);

日志进入/ tmp中的唯一文件,名为msgSends-.

两次运行的文件包含以下输出.

$cat msgSends-72013- __NSMallocBlock__ __NSMallocBlock release- SOUnsafeObject SOUnsafeObject dealloc$cat msgSends-72057- __NSMallocBlock__ __NSMallocBlock release- SOUnsafeObject UIVIEwController release- SOUnsafeObject SOUnsafeObject dealloc

对此没有太多的惊讶.但是,UIVIEwController版本的存在表明UIVIEwController具有release方法的特殊覆盖实现.我想知道为什么?是否可以专门将调用转移到dealloc到主线程?

调试器

是的,这是我想到的第一件事,但我没有证据表明在UIVIEwController中有一个覆盖,所以我经历了我的正常过程.我发现当我跳过步骤时,通常需要更长时间.

无论如何,既然我们知道我们在寻找什么,我就在“休眠”块的最后一行放置一个断点,并使该类继承自UIVIEwController.

当我到达断点时,我添加了一个手动断点…

(lldb) b [UIVIEwController release]Breakpoint 3: where = UIKit`-[UIVIEwController release],address = 0x000000010e814d1a

继续之后,我受到了这个令人敬畏的装配的欢迎,它可以直观地确认正在发生的事情.

pthread_main_np是一个函数,它告诉您是否在主线程上运行.单步执行程序集指令确认我们没有在主线程上运行.

再进一步,我们进入第27行,在那里我们跳过调用dealloc,然后运行你可以很容易看到的代码在主线程上运行dealloc-helper.

你能指望这种前进吗?

由于我无法找到它,我不知道我会一直指望这种情况发生,但它非常方便,显然它们是故意放入代码中的.

每次Apple发布新版本的iOS和OSX时,我都会运行一系列测试.我假设大多数开发人员都做了类似的事情我想我要做的就是编写一个单元测试,并将其添加到该集合中.因此,如果他们改变它,我会立刻知道它出来.

否则,我倾向于认为这可能是可以安全地假设的事情之一.

但是,请注意,子类可能会选择覆盖release(如果它们是在禁用ARC的情况下编译的),并且如果它们不调用基类实现,则不会出现此行为.

因此,您可能希望为您使用的任何第三方视图控制器类编写测试.

我的细节

我只用XCode 6.4,部署目标8.4,模拟iPhone 6进行了测试.我将把测试与其他版本作为读者练习.

顺便说一句,如果您不介意,您发布的示例的详细信息是什么?

总结

以上是内存溢出为你收集整理的ios – 为什么UIViewController在主线程上解除分配?全部内容,希望文章能够帮你解决ios – 为什么UIViewController在主线程上解除分配?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1077466.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存