Strange Exception with GameCenter

2019-04-26 10:50发布

问题:

I keep getting this crash report from GA and users... However i cannot reproduce this exception by testing iphone5,5s,6 with both ios7 and ios8. This issue comes nowhere when application did enter to background. The strange part is that gamecenter will call spritekit, for showing achievement banner?

Does anyone has the same issue?

Last Exception Backtrace:
0   CoreFoundation                  0x23c99e3f __exceptionPreprocess + 127
1   libobjc.A.dylib                 0x31371c8b objc_exception_throw + 38
2   CoreFoundation                  0x23c9f189 -[NSObject(NSObject) doesNotRecognizeSelector:] + 188
3   CoreFoundation                  0x23c9d0a7 ___forwarding___ + 714
4   CoreFoundation                  0x23bcf208 _CF_forwarding_prep_0 + 24
5   SpriteKit                       0x26fe9689 -[SKNode isEqual:] + 164
6   Foundation                      0x248ec9ff +[NSObject(NSDelayedPerforming) cancelPreviousPerformRequestsWithTarget:] + 358
7   GameCenterFoundation            0x2a945873 -[GKPlayer postChangeNotification] + 38
8   GameCenterFoundation            0x2a958d21 __52-[GKDaemonProxy setLocalPlayer:authenticated:reply:]_block_invoke + 848
9   libdispatch.dylib               0x318d18cb _dispatch_call_block_and_release + 10
10  libdispatch.dylib               0x318d18b7 _dispatch_client_callout + 22
11  libdispatch.dylib               0x318d50bf _dispatch_main_queue_callback_4CF + 722
12  CoreFoundation                  0x23c5fbe9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
13  CoreFoundation                  0x23c5e2e9 __CFRunLoopRun + 1512
14  CoreFoundation                  0x23bac621 CFRunLoopRunSpecific + 476
15  CoreFoundation                  0x23bac433 CFRunLoopRunInMode + 106
16  GraphicsServices                0x2af1b0a9 GSEventRunModal + 136
17  UIKit                           0x27197359 UIApplicationMain + 1440
18  MyAppName                       0x001b4f27 main (main.m:16)
19  libdyld.dylib                   0x318f1aaf start + 2

Similar crash issue on iOS8 GKLocalPlayerInternal Unrecognized Selector

I tried a test that using a SKNode and a GKPlayer

SKNode* node = [SKNode node];
GKPlayer* player = [GKLocalPlayer localPlayer];
[node isEqual:player];

It will cause the following exception the same as the above result, which means the system compare the SKNode with [GKPlayer postChangeNotification]....It's really weird.

-[GKLocalPlayerInternal name]: unrecognized selector sent to instance 0x1b6e3f80

回答1:

If as you say you can replicate the crash by simply comparing an SKNode with any GKLocalPlayer then it would seem that this is a bug in SpriteKit as performing an equality check on any object should not result in a crash.

If you have the stamina for it, you could subclass all the SKNode's in your app and implement the isEqual and hash methods yourself. This should avoid the faulty name comparison check on objects that do not implement that selector.

I would also recommend you report this as a bug to Apple.

This question seems to indicate that Apple did indeed update SKNode equality and hashing methods in iOS8, so it is likely the bug was caused by that change.



回答2:

I had similar callstack with SKNode isEqual. THis happens only on IOS 8.1. Had constant crashes, but in different scenario... -[GADSlot name]: unrecognized selector sent to instance

I think the whole thing is triggered when this method is called. +[NSObject(NSDelayedPerforming) cancelPreviousPerformRequestsWithTarget:]

I cannot explain why this would call [SKNode isEqual] (perhaps has some things to cancel), but for me it results in crash. I investigated more around this issue it turned out that was calling my SKScene isEqual. The SKScene isEqual: method is not implemented correctly ( I reported as a bug to apple). Also SKSpriteNode has the same issue for the rest of the SKNode's I didn't tried, but most probably have the same issue.

In my case I added this fix to my subclassed SKScene object. Sure it won't fix globally all the SKNode isEqual, but solved my problem.

   - (BOOL)isEqual:(id)other {

        if (![other isMemberOfClass:[SKScene class]]) {
            return false;
        }
        return [super isEqual:other];
    } 


回答3:

Subclass in Swift:

import SpriteKit

class Scene: SKScene {

    override func isEqual(object: AnyObject?) -> Bool {
        if object?.isKindOfClass(SKScene) == false {
            return false
        }

        return super.isEqual(object)
    }
}

Apply:

class YourScene: Scene {
    // your code
}