views:

79

answers:

2

Recently, I've been getting intermittent error reports from my app claiming "unrecognized selector" in areas that could not possibly cause them, and yet they do.

For example, this one:

Error: NSInvalidArgumentException: *** -[NSCFString didReceiveMemoryWarning]: unrecognized selector sent to instance 0x541fe0
0  CoreFoundation                      0x32de1e23 __handleUncaughtException + 230
1  libobjc.A.dylib                    0x3266d894 _objc_terminate + 156
2  libstdc++.6.dylib                  0x338c3a8c _ZN10__cxxabiv111__terminateEPFvvE + 84
3  libstdc++.6.dylib                  0x338c3b04 _ZSt9terminatev + 24
4  libstdc++.6.dylib                  0x338c3c2c __cxa_throw + 108
5  libobjc.A.dylib                    0x3266be5c objc_exception_throw + 112
6  CoreFoundation                      0x32de2bfd -[NSObject doesNotRecognizeSelector:] + 112
7  CoreFoundation                      0x32d67b19 ___forwarding___ + 480
8  CoreFoundation                      0x32d5e840 _CF_forwarding_prep_0 + 48
9  Foundation                          0x33f765d9 _nsnote_callback + 184
10  CoreFoundation                      0x32d9e511 _CFXNotificationPostNotification + 304
11  Foundation                          0x33f741b3 -[NSNotificationCenter postNotificationName:object:userInfo:] + 70
12  Foundation                          0x33f76519 -[NSNotificationCenter postNotificationName:object:] + 20
13  UIKit                              0x30d18db8 -[UIApplication _performMemoryWarning] + 68
14  UIKit                              0x30d18d50 -[UIApplication _receivedMemoryNotification] + 136
15  UIKit                              0x30d18c80 _memoryStatusChanged + 64
16  CoreFoundation                      0x32d66eb7 __CFNotificationCenterDarwinCallBack + 26
17  CoreFoundation                      0x32d5cb51 __CFMachPortPerform + 78
18  CoreFoundation                      0x32da452b CFRunLoopRunSpecific + 2302
19  CoreFoundation                      0x32da3c1f CFRunLoopRunInMode + 50
20  GraphicsServices                    0x31bb9374 GSEventRunModal + 196
21  UIKit                              0x30bf3c30 -[UIApplication _run] + 560
22  UIKit                              0x30bf2230 UIApplicationMain + 968
23  Mind                                0x00002c68 main + 72
24  Mind                                0x00002be4 start + 52

This is the OS sending a memory warning to my app, and somehow the application class has changed into a string.

It seems to happen a lot more when the code is invoked via an NSOperation:

Error: NSInvalidArgumentException: -[NSCFString setObject:forKey:]: unrecognized selector sent to instance 0x3e793088
9  Mind                                0x0015de70 -[CCTextureCache textureFromFile:] + 528
10  Mind                                0x0015d9f4 -[CCTextureCache loadImageUncached:pixelFormat:] + 116
11  Mind                                0x0015d058 -[CCTextureCache addImage:pixelFormat:] + 152
12  Mind                                0x00080524 -[ImageLoader imageWithFile:pixelFormat:] + 84
13  Mind                                0x000854c4 -[ImageLoadOperation performLoad] + 68
14  Mind                                0x00085800 -[ResourceLoadOperation main] + 112
15  Foundation                          0x30c4c8b5 -[__NSOperationInternal start] + 664
16  Foundation                          0x30c4c613 -[NSOperation start] + 22
17  Foundation                          0x30cbdb63 ____startOperations_block_invoke_2 + 46
18  libSystem.B.dylib                  0x31227858 _dispatch_call_block_and_release + 20
19  libSystem.B.dylib                  0x3122863c _dispatch_worker_thread2 + 128
20  libSystem.B.dylib                  0x311b1544 _pthread_wqthread + 400
21  libSystem.B.dylib                  0x311a8b74 __stack_chk_fail + 4294967295

The code in question is:

[textures setObject:texture forKey:filename];

textures is type NSMutableDictionary* and never gets reassigned or deallocated (naturally, since this is a cache object). This is the only place where setObject is invoked in this method, yet according to the stack trace, textures was a string.

I also get this weirdness:

Error: NSInvalidArgumentException: -[NSConcreteNotification getPixelFormatForIdentifier:]: unrecognized selector sent to instance 0x5c021b0
9  Mind                                0x0015dd0c -[CCTextureCache textureFromFile:] + 172
10  Mind                                0x0015d9f4 -[CCTextureCache loadImageUncached:pixelFormat:] + 116
11  Mind                                0x0015d058 -[CCTextureCache addImage:pixelFormat:] + 152
12  Mind                                0x00080524 -[ImageLoader imageWithFile:pixelFormat:] + 84
13  Mind                                0x000854c4 -[ImageLoadOperation performLoad] + 68
14  Mind                                0x00085800 -[ResourceLoadOperation main] + 112
15  Foundation                          0x347b78b5 -[__NSOperationInternal start] + 664
16  Foundation                          0x347b7613 -[NSOperation start] + 22
17  Foundation                          0x34828b63 ____startOperations_block_invoke_2 + 46
18  libSystem.B.dylib                  0x32a2f858 _dispatch_call_block_and_release + 20
19  libSystem.B.dylib                  0x32a3063c _dispatch_worker_thread2 + 128
20  libSystem.B.dylib                  0x329b9544 _pthread_wqthread + 400
21  libSystem.B.dylib                  0x329b0b74 __stack_chk_fail + 4294967295

This trace is from the following code in CCTextureCache:

CCTexture2DPixelFormat pixelFormat = [self getPixelFormatForIdentifier:identifier];

How CCTextureCache changed into NSConcreteNotification after having already called a number of methods on itself is baffling to say the least.

Has anyone else noticed this sort of thing? Am I somehow getting memory corruption?

A: 

This is often caused by memory management errors. Did you try zombies?

...with zombies enabled, messages to deallocated objects will no longer behave strangely or crash in difficult-to-understand ways, but will instead log a message and die in a predictable and debugger-breakpointable way. This is the tool to use when trying to track down over-releases and premature releases.

zoul
This app is in the app store at the moment. Out of a couple thousand users I get an error report of this sort every few days. I've not encountered a zombie in my development environment.
Karl
Am I the only one who finds "I've not encountered a zombie in my development environment." funny?
Douwe Maan
I have not studied your samples too long, but such bugs are usually quite a pain to debug. Try the static analyzer, double-check your memory management, assert as much as you can, nil freed references and turn on the zombies in debug builds :)
zoul
I just spent 1.5 days chasing down a really nasty bug where a Class object was being corrupted due to a rogue pointer - so it is possible. Mine was much more reproducible than yours - if yours is a memory corruption issue it could be tough to find. You can use gdb's "watch" command to set a breakpoint on write access to a given address, which is in my experience the only useful tool for finding these bugs. The hard bit is working out which location to watch. Before you do that, though, I'd definitely check all your code for warnings and run the static analyzer.
Echelon
Yeah that's gonna be a bit of a problem. My code generates no warnings (I'm quite anal about that), and the static analyzer finds nothing (I'm anal about that, too). The static analyzer can never supplant good desk checking, of course. I only use factory constructors wherever possible, and if I'm forced to use the init/alloc idiom I make sure to autorelease. The program does no pointer walking, type coercion, or arithmetic, either. The worst part is, the issue has only ever surfaced in the wild, so all I ever get is a stack trace =/
Karl
A: 

Did you check some race conditions about multi-threads ? It seems like some resource freed by another thread, and current thread send a messaged to a deallocated object.

Error: NSInvalidArgumentException: -[NSCFString setObject:forKey:]: unrecognized selector sent to instance 0x3e793088
9  Mind                                0x0015de70 -[CCTextureCache textureFromFile:] + 528
10  Mind                                0x0015d9f4 -[CCTextureCache loadImageUncached:pixelFormat:] + 116
11  Mind                                0x0015d058 -[CCTextureCache addImage:pixelFormat:] + 152
12  Mind                                0x00080524 -[ImageLoader imageWithFile:pixelFormat:] + 84
13  Mind                                0x000854c4 -[ImageLoadOperation performLoad] + 68
14  Mind                                0x00085800 -[ResourceLoadOperation main] + 112
15  Foundation                          0x30c4c8b5 -[__NSOperationInternal start] + 664
16  Foundation                          0x30c4c613 -[NSOperation start] + 22
17  Foundation                          0x30cbdb63 ____startOperations_block_invoke_2 + 46
18  libSystem.B.dylib                  0x31227858 _dispatch_call_block_and_release + 20
19  libSystem.B.dylib                  0x3122863c _dispatch_worker_thread2 + 128
20  libSystem.B.dylib                  0x311b1544 _pthread_wqthread + 400
21  libSystem.B.dylib                  0x311a8b74 __stack_chk_fail + 4294967295
Toro
The thing is, the objects that are getting overwritten are singletons that by this time have already existed for a long time (in the third example I cited, the singleton object class gets overwritten WHILE it's 3 levels deep in self-referencing calls). A race condition could only cause allocs to overwrite other allocs that happen very close to each other in different threads. It wouldn't overwrite long established objects. It seems that it would have to be a rogue pointer somewhere, but it's odd that the memory corruption always leads to a valid object class descriptor.
Karl
At the 16. I see NSOperation start, so I guess it runs under multi-threads.
Toro
And the textures means OpenGL-ES texture? Can the OpenGL-ES run under multi-thread env?
Toro
Yes. The OpenGL stuff is in the cocos2d library. The code that is running at that time is a background texture load operation (the only thing that runs in a different thread).
Karl
I remember it needs some skill to generate textures under multi-thread, something like to make another opengl context for child thread. As for me, if it runs not under main thread to generate textures, it always crash my application.
Toro