4

I use NSProgress to notify about progress of a complicated task (the task consist of many subtasks, that can consist of other subtasks).
I create main progress in startTask method:

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

There is no currentProgress:

(lldb) po [NSProgress currentProgress]
nil

Sometimes I get crash with strange 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 in +[NSProgress progressWithTotalUnitCount:] ()

This code executes in mainThread.
Why does this happens and what I can do to avoid this?

Andrew Romanov
  • 4,774
  • 3
  • 25
  • 40

2 Answers2

6

Seems that you have one or more NSProgress objects released without removing their Key-value Observers.

You can take a look at this: https://github.com/AFNetworking/AFNetworking/issues/3710

Comments from '0xced' clearly figured out why this could happen:

That’s some interesting data.

NSProgress instance are sometimes reassigned an address previously used by another NSProgress instance: for example, 0x7ffa6a119730 is assigned to one download progress, then one upload progress, then another download progress (where the crash happens). This explains why running with zombies enabled made the crash disappear: addresses are never reused because deallocated objets turn into zombies and keep their original address forever.

When the same address (0x7ffa6a119730) is reused, the NSProgress instance has an observer right after its creation. That observer is 0x7ffa68c0bab0, an old AFURLSessionManagerTaskDelegate instance.

Community
  • 1
  • 1
Wolphy
  • 83
  • 1
  • 5
0

This crash happens in my app in all versions of iOS since it was built (since iOS 10).

My progress reporting implementation is similar to what mentioned in Apple's WWDC 2015 talk - Best Practices for Progress Reporting where progress objects are nested by calling parentProgress.addChild(childProgress, withPendingUnitCount: value) as parent tasks create new child tasks and add to the dispatch queue.

I assume, when parent progress object is deallocated, it will stop observing child progress change (there's no public remove child method). This should be true but I suspect the implementation is not thread-safe.

Anyhoo, I modified my implementation to use 1 single progress object to avoid issues with multiple progress objects.

billibala
  • 321
  • 4
  • 14