Crash: [NSTaggedPointerString count]: unrecognized selector sent to instance 0x8000000000000000

Hello guys,

I am trying to debug the following crash: [NSTaggedPointerString count]: unrecognized selector sent to instance 0x8000000000000000

Fatal Exception: NSInvalidArgumentException
0  CoreFoundation                 0x19af619d8 __exceptionPreprocess
1  libobjc.A.dylib                0x1af2e7b54 objc_exception_throw
2  CoreFoundation                 0x19ae71bbc -[NSOrderedSet initWithSet:copyItems:]
3  CoreFoundation                 0x19af6401c ___forwarding___
4  CoreFoundation                 0x19af65f8c _CF_forwarding_prep_0
5  libswiftCore.dylib             0x19e9e4d70 Dictionary._Variant.updateValue(_:forKey:)
... More lines...
24 MyApplication                          0x10424c23c main + 15 (AppDelegate.swift:15)
25 libdyld.dylib                  0x19abb9568 start

The thing is that the stack trace and the crash reason changes in each report, some of the crash reason I have seen are:

  • [NSTaggedPointerString count]: unrecognized selector sent to instance 0x8000000000000000
  • -[__NSCFNumber count]: unrecognized selector sent to instance 0x8000000000000000
  • EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000010

This seems to be related to some thread safety issues based on some other threads: https://developer.apple.com/forums/thread/116028

I am attaching an implementation sample of what the code is doing right now

Basically, the code where this crash is happening is using a DispatchGroup object, before calling the .leave() method, this is updating a dictionary property that was declared before. At that point, the code seems to be running on another thread which is causing the issue.

If this is a thread issue, what are my options here? What would be the easiest thing to fix this issue?

Thanks in advance!!

Replies

This is almost certainly a thread-safety issue but it’s hard to spot the error based on the code snippet you posted. Specifically, that code has no concurrency in it at all, just some comments where the concurrency would be )-: So, I’ve rewritten it to reflect what I think is going on:

func properties() {
    let group = DispatchGroup()

    group.enter()
    someQueue.async {
        let value = … some calculation …
        self.data.updateValue(value, forKey: "my_wonderful_key")
        group.leave()
    }
    
    group.enter()
    someQueue.async {
        let value2 = … some calculation …
        self.data.updateValue(value2, forKey: "my_wonderful_key_2")
        group.leave()
    }

    group.notify(queue: DispatchQueue.main) {
        print("Yay. Operation completed!")
    }
}

Is that something like your real code?

Also, some questions:

  • Is properties() called on the main thread?

  • Who else accesses data? More specifically, on what thread are those accesses being done?

Right now data a write-only variable, which presuming doesn’t reflect reality (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yes, properties() is called on the main thread. Also data is being accessed from the main thread, and from a background thread that corresponds to the first async work.


func properties() {
    let group = DispatchGroup()

    group.enter()
    UNUserNotificationCenter.current().getNotificationSettings { (notification) in 
        let value = … some calculation …
        self.data.updateValue(value, forKey: "my_wonderful_key")
        group.leave()
    }
    
    group.enter()
    MyClass.someMethod { (granted) in
        let value2 = … some calculation …
        self.data.updateValue(value2, forKey: "my_wonderful_key_2")
        group.leave()
    }

    group.notify(queue: DispatchQueue.main) {
        print("Yay. Operation completed!")
        completionHandler(self.data)
   }
}

As you can see, in the first block, data is being accessed in the getNotificationSettings completionHandler which is executed on a background thread. On the other hand, in the other two (2) blocks data is being accessed on the main thread, since those two blocks MyClass.someMethod and group.notify run in the main queue.

One more thing is that I have crash reports for all these 3 lines:

  • self.data.updateValue(value, forKey: "my_wonderful_key")
  • self.data.updateValue(value2, forKey: "my_wonderful_key_2")
  • completionHandler(self.data)

So I wonder if the fact that data is accessed from a background thread in the first block is causing that the app is crashing then when the same data is accessed from the main thread in the block 2 and 3?

if that would be the cause, just wrapping the code with an async block in the main queue would work? That way the data is only accessed from the main thread

    group.enter()
    UNUserNotificationCenter.current().getNotificationSettings { (notification) in 
        let value = … some calculation …
        DispatchQueue.main.async { 
            self.data.updateValue(value, forKey: "my_wonderful_key")
            group.leave()
     }
       

Thanks!

As you can see, in the first block, data is being accessed in the getNotificationSettings completion handler which is executed on a background thread.

OK, that’s not safe (assuming data is a Swift dictionary). Swift’s ownership rule is that a value can be simultaneously accessed by multiple readers or a single writer. In this case the data dictionary is being accessed by multiple writers (your first and second blocks) and possibly one reader (depending on whether self.data is public). That’s not following the rules and will result in weird concurrency problems.

If I were in your shoes I’d make self.data a main-thread-only value. You can do this by making value and value2 variables within the properties method and then have your third block, which is running on the main thread, commit those to self.data. For example:

func properties() {
    let group = DispatchGroup()

    var value: SomeType? = nil
    group.enter()
    someQueue.async {
        value = … some calculation …
        group.leave()
    }
    
    var value2: SomeType2? = nil
    group.enter()
    someQueue.async {
        value2 = … some calculation …
        group.leave()
    }

    group.notify(queue: DispatchQueue.main) {
        self.data.updateValue(value!, forKey: "my_wonderful_key")
        self.data.updateValue(value2!, forKey: "my_wonderful_key_2")
        print("Yay. Operation completed!")
    }
}

The use of value is safe because:

  • The initialisation on line 4 is guaranteed to be done before the async write on line 7 because… well… obviously.

  • The async write on line 7 is guaranteed to be done before the read on line 19 before of the Dispatch group.

Likewise for value2.

I have to say that Swift concurrency will make this all a lot nicer (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"