Nat! bio photo

Nat!

Senior Mull

Twitter Github Twitch

(Buggy ?) Instruments tries to trick me

Here is a small Xcode test project that will successfully run standalone or in the debugger, but will crash when run with Instruments.

SomethingBadHappened.zip

(In Xcode use the "Run" menu, choose "Run with Performance Tool" and select "Leaks"). What this project is doing, is overloading a category in Foundation with a method, that is itself stored in a Framework. In a condensed form it looks like this.

// defined in a framework
@implementation NSObject( NSUnpublishedEOFDebug)

- (void) addObject:(id) obj
toBothSidesOfRelationshipWithKey:(NSString *) key
{
   NSLog( @"%@ %s: %@ %@",
          self, __PRETTY_FUNCTION__, obj, key);
   NSLog( @"Denied!");
}

@end
and
// defined in a tool, linking to the framework
int main (int argc, const char * argv[])
{
   [@"foo" addObject:@"bar"
toBothSidesOfRelationshipWithKey:@"baz"];
}
And this is what you will see, if you are using Xcode 3.2.2.
Normal run

+[NSObject( NSUnpublishedEOFDebug) load]
foo -[NSObject(NSUnpublishedEOFDebug) addObject:toBothSidesOfRelationshipWithKey:]: bar baz
Denied!

Instruments run

+[NSObject( NSUnpublishedEOFDebug) load]
*** WARNING: -[NSObject(NSKeyValueCoding) takeValue:forKey:] is deprecated. It will be removed in a future release and should no longer be used.
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSCFString 0x2024> takeValue:forKey:]: attempt to assign value to unknown key: 'baz'.
This class does not have an instance variable of the name baz or _baz, nor a method of the name setBaz, or _setBaz'
*** Call stack at first throw:
(
0 CoreFoundation 0x9440abba __raiseError + 410
1 libobjc.A.dylib 0x94f4d509 objc_exception_throw + 56
2 CoreFoundation 0x944559f1 -[NSException raise] + 17
3 Foundation 0x9833a179 _raiseKeyValueException + 234
4 SomethingBadHappened 0x00001ef7 main + 125
5 SomethingBadHappened 0x00001e71 start + 53
)
<End of Run>

Some Thoughts

  • Does not crash with Shark
  • Yes, Foundation still has some old EOF methods contained in a category called NSUnpublishedEOF.
  • If the category is not part of the test framework, but part of the tool. There is no such problem.
  • Since, the test framework is dependent on Foundation, Foundation must be loaded first.
  • It's as if by a accident Foundation was loaded again, and it's categories override the test framework categories.