4

I got something really strange: a method causes unrecognized selector sent to instance, but doesn't NSLog() anything at all.

I made a custom NSControl subclass, where I try to draw my custom cells like this:

- (void)drawRect:(NSRect)dirtyRect {
    NSLog(@"DrawRect entered!");
    [[NSColor grayColor] set];
    [NSBezierPath fillRect:[self bounds]];

    unsigned int i, count = [cells count];
    NSRect cellRect = NSMakeRect(0, 0, ([self bounds]).size.width, cellHeight);
    for (i = 0; i < count; i++) {
        NSLog(@"Drawing cell %d at: %@", i, NSStringFromRect(cellRect));
        MKMenuCell *cell = [cells objectAtIndex:i];
        [cell drawWithFrame:cellRect inView:self];
        cellRect.origin.y += cellHeight;
    }
}

And -[MKMenuCell drawWithFrame:inView:]:

- (void)drawWithFrame:(NSRect)bounds inView:(NSView *)controlView {
    NSLog(@"-drawWithFrame:inView:");
    NSMutableDictionary *strAttribs = [[NSMutableDictionary alloc] init];
    [strAttribs setObject:[NSColor blackColor] forKey:NSFontAttributeName];
    [name drawInRect:bounds withAttributes:strAttribs]; // "unrecognized selector sent ..." is caused by this method call.
}

The output:

2013-03-11 18:46:54.823 MacOverflow[738:a0f] DrawRect entered!
2013-03-11 18:46:54.826 MacOverflow[738:a0f] Drawing cell 0 at: {{0, 0}, {176, 30}}
2013-03-11 18:46:54.826 MacOverflow[738:a0f] -drawWithFrame:inView:
2013-03-11 18:46:54.827 MacOverflow[738:a0f] Drawing cell 1 at: {{0, 30}, {176, 30}}
2013-03-11 18:46:54.828 MacOverflow[738:a0f] -drawWithFrame:inView:
2013-03-11 18:46:54.828 MacOverflow[738:a0f] -[NSCachedWhiteColor screenFontWithRenderingMode:]: unrecognized selector sent to instance 0x100512920
2013-03-11 18:46:54.829 MacOverflow[738:a0f] An uncaught exception was raised
2013-03-11 18:46:54.831 MacOverflow[738:a0f] -[NSCachedWhiteColor screenFontWithRenderingMode:]: unrecognized selector sent to instance 0x100512920
2013-03-11 18:46:54.834 MacOverflow[738:a0f] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCachedWhiteColor screenFontWithRenderingMode:]: unrecognized selector sent to instance 0x100512920'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x00007fff88c26784 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x00007fff858ddf03 objc_exception_throw + 45
    2   CoreFoundation                      0x00007fff88c80110 +[NSObject(NSObject) doesNotRecognizeSelector:] + 0
    3   CoreFoundation                      0x00007fff88bf88ef ___forwarding___ + 751
    4   CoreFoundation                      0x00007fff88bf4a38 _CF_forwarding_prep_0 + 232
    5   AppKit                              0x00007fff89afcfae +[NSStringDrawingTextStorage _fastDrawString:attributes:length:inRect:graphicsContext:baselineRendering:usesFontLeading:usesScreenFont:typesetterBehavior:paragraphStyle:lineBreakMode:boundingRect:padding:scrollable:] + 402
    6   AppKit                              0x00007fff896db539 _NSStringDrawingCore + 1588
    7   MacOverflow                         0x00000001000159b7 -[MKMenuCell drawWithFrame:inView:] + 229
    8   MacOverflow                         0x0000000100015f1e -[MKMenuControl drawRect:] + 595
    9   AppKit                              0x00007fff896d6cc5 -[NSView _drawRect:clip:] + 3390
    10  AppKit                              0x00007fff896d5938 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 1325
    11  AppKit                              0x00007fff896d5ca2 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2199
    12  AppKit                              0x00007fff896d5ca2 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2199
    13  AppKit                              0x00007fff896d5ca2 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2199
    14  AppKit                              0x00007fff896d400a -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 767
    15  AppKit                              0x00007fff896d3b2c -[NSThemeFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 254
    16  AppKit                              0x00007fff896d03de -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] + 2683
    17  AppKit                              0x00007fff89649c0e -[NSView displayIfNeeded] + 969
    18  AppKit                              0x00007fff89611c3b -[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 1050
    19  AppKit                              0x00007fff896117d2 -[NSWindow orderWindow:relativeTo:] + 94
    20  AppKit                              0x00007fff895dd974 -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] + 1726
    21  AppKiProgram received signal:  “SIGABRT”.
t                              0x00007fff895dba91 loadNib + 226
    22  AppKit                              0x00007fff895dafa1 +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] + 248
    23  AppKit                              0x00007fff895dadd9 +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 326
    24  AppKit                              0x00007fff895d835b NSApplicationMain + 279
    25  MacOverflow                         0x0000000100000ef9 main + 33
    26  MacOverflow                         0x0000000100000ed0 start + 52
    27  ???                                 0x0000000000000001 0x0 + 1
)
terminate called after throwing an instance of 'NSException'

As you can see MKMenuCell's drawWithFrame:inView: is called. but the output from its NSLog() statement isn't displayed. What is happening here? How to solve it?

EDIT: I now realize, looking at the GDB output, the first time nothing goes wrong. This makes it even stranger.

11684
  • 7,356
  • 12
  • 48
  • 71
  • `([self bounds]).size.width` - Argh, why not `self.bounds.size.width`? –  Mar 11 '13 at 17:55
  • Or: `CGRectGetWidth(self.bounds)`. – Adam Mar 11 '13 at 18:08
  • @H2CO3 Sorry, I prototyped this code on a machine with Objective-C 1... But I think I already found the issue (not yet tested): I set the color for the key `NSFontAttributeName`. NSString looks the value up for `NSFontAttributeName`, thinks it is an NSFont (but it is an NSColor). Hence the unrecognised selector. The error output blocks the stdout stream for my NSLog() statement. But this is all untested. I'll leave the question here, so other people can see the code; it may have multiple issues. – 11684 Mar 11 '13 at 18:26

1 Answers1

9

The bad selector being sent, screenFontWithRenderingMode, is a method on NSFont. I'm betting that you shouldn't be setting a color for the value of the key NSFontAttributeName. You should probably give it a font.

Tom Hamming
  • 10,577
  • 11
  • 71
  • 145
  • I already spotted that (see my comment on the question), but left the question here because I wasn't sure if this was the only issue. – 11684 Mar 11 '13 at 18:41
  • @11684: It's not that the problem “blocks the stdout stream for my NSLog() statement”; it's that it causes an exception, which ends execution of your code and drops your app back to the innermost run loop (or, in some cases, terminates it entirely). Your inner method did not progress beyond your attempted `drawInRect:withAttributes:`; your outer method only entered the loop once. Fixing the exception (by not putting a color where a font is expected) will fix the loop. – Peter Hosey Mar 11 '13 at 20:51
  • Well, you're wrong (that sounds rude, but it isn't meant to. I'm not a native English speaker). The loop gets run twice (because the first value in the array is nil, another issue with the code) and the NSLog statement is before the line that throws the error. I think `stdout` buffers that, and then `stderr` butts in. After that the app terminates without flushing the buffer of `stdout`. – 11684 Mar 11 '13 at 21:02
  • @11684: You're correct that the loop runs twice instead of once; I didn't see that. By “the first value in the array”, you must mean the name of the first cell, because the first cell itself cannot be `nil`—arrays cannot contain `nil`. That being acknowledged, my explanation stands: as soon as you send `drawInRect:withAttributes:` to an actual string that will try to use your invalid attributes, which you currently do in the second cell, the string will throw an exception, which will break out of the loop—indeed, out of your code entirely—at that point. – Peter Hosey Mar 12 '13 at 02:05
  • Hey, the log I printed contains more logs than the first one. I'm sorry, I based my comments on another log (I ran this code hundreds of times because I didn't understand what went wrong), and that log happened to be a tiny bit different... My apologies! – 11684 Mar 12 '13 at 08:31