4

我使用 NSProgress 通知复杂任务的进度(该任务由许多子任务组成,可以由其他子任务组成)。
我在 startTask 方法中创建了主要进度:

- (void)_startTask
{
    _progress = [NSProgress progressWithTotalUnitCount:100]; //sometimes crash is here
    <...>
}

没有当前进度:

(lldb) po [NSProgress currentProgress]

有时我会因奇怪的 stackTrace 崩溃:

0: 0x000000018539a7e8 in object_isClass ()
1: 0x0000000186b39fe0 in KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED ()
2: 0x0000000186b384bc in
NSKeyValueWillChangeWithPerThreadPendingNotifications ()
3: 0x0000000186cac9c0 in -[NSProgress _setValueForKeys:settingBlock:] ()
4: 0x0000000186cacc7c in -[NSProgress setTotalUnitCount:] ()
5: 0x0000000186cab718在 +[NSProgress progressWithTotalUnitCount:] ()

此代码在 mainThread 中执行。
为什么会发生这种情况,我可以做些什么来避免这种情况?

4

2 回答 2

5

似乎您释放了一个或多个 NSProgress 对象而没有删除它们的键值观察者。

你可以看看这个: https ://github.com/AFNetworking/AFNetworking/issues/3710

'0xced' 的评论清楚地说明了为什么会发生这种情况:

这是一些有趣的数据。

NSProgress 实例有时会被重新分配一个以前由另一个 NSProgress 实例使用的地址:例如,0x7ffa6a119730 被分配给一个下载进度,然后是一个上传进度,然后是另一个下载进度(发生崩溃的地方)。这就解释了为什么在启用僵尸的情况下运行会使崩溃消失:地址永远不会被重用,因为解除分配的对象会变成僵尸并永远保留其原始地址。

当重用相同的地址(0x7ffa6a119730)时,NSProgress 实例在创建后立即具有观察者。那个观察者是 0x7ffa68c0bab0,一个旧的 AFURLSessionManagerTaskDelegate 实例。

于 2018-03-13T08:11:56.833 回答
0

自构建以来(自 iOS 10 起),此崩溃发生在我的所有 iOS 版本中的应用程序中。

我的进度报告实现类似于 Apple 的 WWDC 2015 演讲 -进度报告的最佳实践parentProgress.addChild(childProgress, withPendingUnitCount: value)中提到的内容,其中通过调用父任务创建新的子任务并添加到调度队列来嵌套进度对象。

我假设,当父进度对象被释放时,它将停止观察子进度变化(没有公共remove child方法)。这应该是真的,但我怀疑实现不是线程安全的。

Anyhoo,我修改了我的实现以使用 1 个单个进度对象来避免多个进度对象的问题。

于 2019-01-03T09:33:21.723 回答