Qustion about possibly bad advice in NSView documentation

  • The conceptual documentation for NSView contains the following note:
      http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaViewsGuide/S
    ubclassingNSView/chapter_5_section_3.html


      "Note: The implementation of the NSView class before Mac OS X v10.4.3 could discard any rectangles marked as needing display within a subclass's implementation of drawRect:. For maximum compatibility, when marking areas as requiring display from within the drawRect: method it is best to call the view's setNeedsDisplayInRect: method using the NSObject instance method performSelector:withObject:afterDelay:."

      Um, isn't this supremely bad advice ? performSelector:withObject:afterDelay:. requires an _object_ argument.  How exactly is an NSRect structure going to be passed as the argument to performSelector:withObject:afterDelay:?

      I think the note should have stated, "don't call -setNeedsDisplayInRect: from within an implementation of -drawRect: because some versions of Mac OS X ignore changes to invalid areas from within -drawRect."  Even more advice would be "limit the code within an implementation of -drawRect: to code that actually draws.  Any other code such as application logic to determine what should be drawn is best implemented elsewhere because -drawRect: will be called automatically by Cocoa whenever a view needs to be drawn, and you may not be able to predict all of those times."
  • You can wrap primitves using the NSValue class and pass it that way
    as an object.

    Regards,
    Dominik

    On 25.09.2007, at 16:08, Erik Buck wrote:

    > The conceptual documentation for NSView contains the following note:
    > http://developer.apple.com/documentation/Cocoa/Conceptual/
    > CocoaViewsGuide/SubclassingNSView/chapter_5_section_3.html
    >
    > "Note: The implementation of the NSView class before Mac OS X
    > v10.4.3 could discard any rectangles marked as needing display
    > within a subclass's implementation of drawRect:. For maximum
    > compatibility, when marking areas as requiring display from within
    > the drawRect: method it is best to call the view's
    > setNeedsDisplayInRect: method using the NSObject instance method
    > performSelector:withObject:afterDelay:."
    >
    > Um, isn't this supremely bad advice ?
    > performSelector:withObject:afterDelay:. requires an _object_
    > argument.  How exactly is an NSRect structure going to be passed as
    > the argument to performSelector:withObject:afterDelay:?
    >
    > I think the note should have stated, "don't call -
    > setNeedsDisplayInRect: from within an implementation of -drawRect:
    > because some versions of Mac OS X ignore changes to invalid areas
    > from within -drawRect."  Even more advice would be "limit the code
    > within an implementation of -drawRect: to code that actually
    > draws.  Any other code such as application logic to determine what
    > should be drawn is best implemented elsewhere because -drawRect:
    > will be called automatically by Cocoa whenever a view needs to be
    > drawn, and you may not be able to predict all of those times."
  • Indeed, you can wrap NSRect structures in an NSValue object.  That doesn't help in this case.  The documentation seems to be suggesting that the following is valid (I don't think it is):

      - (void)drawRect:(NSRect)aRect
      {
        [self performSelector:@selector(setNeedsDisplayInRect:) withObject:NSMakeRect(0,0, 100, 100) afterDelay:0.0];
      }


      Even if this is changed to use NSValue, it doesn't help:

      - (void)drawRect:(NSRect)aRect
      {
        [self performSelector:@selector(setNeedsDisplayInRect:) withObject:[NSValue valueWithRect:NSMakeRect(0,0, 100, 100)] afterDelay:0.0];
      }

      I doesn't help because the setNeedsDisplayInRect: method is not expecting an NSValue argument; it's expecting an NSRect structure argument.


    Dominik Pich <Dominik...> wrote:
        You can wrap primitves using the NSValue class and pass it that way
    as an object.
  • On Sep 25, 2007, at 8:11 AM, Erik Buck wrote:

    > Indeed, you can wrap NSRect structures in an NSValue object.  That
    > doesn't help in this case.  The documentation seems to be
    > suggesting that the following is valid (I don't think it is):
    >
    > - (void)drawRect:(NSRect)aRect
    > {
    > [self performSelector:@selector(setNeedsDisplayInRect:)
    > withObject:NSMakeRect(0,0, 100, 100) afterDelay:0.0];
    > }
    >
    >
    > Even if this is changed to use NSValue, it doesn't help:
    >
    > - (void)drawRect:(NSRect)aRect
    > {
    > [self performSelector:@selector(setNeedsDisplayInRect:)
    > withObject:[NSValue valueWithRect:NSMakeRect(0,0, 100, 100)]
    > afterDelay:0.0];
    > }
    >
    > I doesn't help because the setNeedsDisplayInRect: method is not
    > expecting an NSValue argument; it's expecting an NSRect structure
    > argument.

    Provide a setNeedsDisplayInRect:(NSValue*) in the target object that
    knows how to deal with the NSValue encoded NSRect.

    ...or use a trampoline object to do this...

    [[self performLater] setNeedsDisplayInRect:NSMakeRect(0,0, 100, 100)];

    ... of course you have to implement the magic to do the above. You
    can find some code online for this type of thing (HOM, higher order
    messaging).

    -Shawn
  • Hi, you verfied this? I was under the impressions that
    performSelector same as NSInvocation transparently 'unwraps'
    NSValues........or maybe only NSInvocation!?
    well that's what I understood and that may be totally wrong :)
    IF it is: sorry for bad advice :)

    Regards,
    Dominik
    On 25.09.2007, at 17:11, Erik Buck wrote:

    > Indeed, you can wrap NSRect structures in an NSValue object.  That
    > doesn't help in this case.  The documentation seems to be
    > suggesting that the following is valid (I don't think it is):
    >
    > - (void)drawRect:(NSRect)aRect
    > {
    > [self performSelector:@selector(setNeedsDisplayInRect:)
    > withObject:NSMakeRect(0,0, 100, 100) afterDelay:0.0];
    > }
    >
    >
    > Even if this is changed to use NSValue, it doesn't help:
    >
    > - (void)drawRect:(NSRect)aRect
    > {
    > [self performSelector:@selector(setNeedsDisplayInRect:)
    > withObject:[NSValue valueWithRect:NSMakeRect(0,0, 100, 100)]
    > afterDelay:0.0];
    > }
    >
    > I doesn't help because the setNeedsDisplayInRect: method is not
    > expecting an NSValue argument; it's expecting an NSRect structure
    > argument.
    >
    >
    > Dominik Pich <Dominik...> wrote:
    > You can wrap primitves using the NSValue class and pass it that
    > way
    > as an object.
  • On Sep 25, 2007, at 10:25 AM, Shawn Erickson wrote:

    >
    > On Sep 25, 2007, at 8:11 AM, Erik Buck wrote:
    >
    >> Indeed, you can wrap NSRect structures in an NSValue object.  That
    >> doesn't help in this case.  The documentation seems to be
    >> suggesting that the following is valid (I don't think it is):
    >>
    >> - (void)drawRect:(NSRect)aRect
    >> {
    >> [self performSelector:@selector(setNeedsDisplayInRect:)
    >> withObject:NSMakeRect(0,0, 100, 100) afterDelay:0.0];
    >> }
    >>
    >>
    >> Even if this is changed to use NSValue, it doesn't help:
    >>
    >> - (void)drawRect:(NSRect)aRect
    >> {
    >> [self performSelector:@selector(setNeedsDisplayInRect:)
    >> withObject:[NSValue valueWithRect:NSMakeRect(0,0, 100, 100)]
    >> afterDelay:0.0];
    >> }
    >>
    >> I doesn't help because the setNeedsDisplayInRect: method is not
    >> expecting an NSValue argument; it's expecting an NSRect structure
    >> argument.
    >
    > Provide a setNeedsDisplayInRect:(NSValue*) in the target object
    > that knows how to deal with the NSValue encoded NSRect.
    >
    > ...or use a trampoline object to do this...
    >
    > [[self performLater] setNeedsDisplayInRect:NSMakeRect(0,0, 100, 100)];
    >
    > ... of course you have to implement the magic to do the above. You
    > can find some code online for this type of thing (HOM, higher order
    > messaging).

    You could also make an NSInvocation and have _that_ performed.

    Roughly typed in mail:

    NSMethodSignature *sig = [self methodSignatureForSelector:@selector
    (setNeedsDisplayInRect:)];
    NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:
    sig];
    [invoc setSelector: @selector(setNeedsDisplayInRect:)];
    NSRect rect = NSMakeRect(0,0,100,100);
    [invoc setArgument: &rect atIndex: 2];
    [invoc performSelector: @selector(invokeWithTarget:) withObject:
    self afterDelay: 0.0];

    Glenn Andreas                      <gandreas...>
      <http://www.gandreas.com/> wicked fun!
    quadrium2 | build, mutate, evolve, animate  | images, textures,
    fractals, art
  • The following is absolutely feasible; it's just not what the documentation seems to be suggesting:

      - (void)setNeedsDisplayInRectWrappedInValue:(NSValue *)aValue
      {
        [self setNeedsDisplayInRect:[aValue rectValue]];
      }


      - (void)drawRect:(NSREct)aRect
      {
        [self performSelector:@selector(setNeedsDisplayInRectWrappedInValue:) withObject:[NSValue valueWithRect:NSMakeRect(0,0,100,100)] afterDelay:0.0];
      }

      I think the documentation is giving bad advice, and the easiest and best solution is to NOT try and invalidate rects from within -drawRect: at all thus avoiding the entire issue.
  • On Sep 25, 2007, at 8:40 AM, Erik Buck wrote:

    > I think the documentation is giving bad advice, and the easiest and
    > best solution is to NOT try and invalidate rects from within -
    > drawRect: at all thus avoiding the entire issue.

    So use the box at the bottom of the page to report what you think is
    wrong with the documentation. :)

    -Shawn
  • On 25 Sep 2007, at 16:26, Dominik Pich wrote:

    > Hi, you verfied this? I was under the impressions that
    > performSelector same as NSInvocation transparently 'unwraps'
    > NSValues........or maybe only NSInvocation!?

    I think you're imagining things here.  Why would -
    performSelector:withObject: ever want to do that?  For a start, you
    wouldn't be able to pass an NSValue.  Also, that kind of behaviour
    doesn't fit the name of the method... you're asking it to perform a
    selector *with an object* (i.e. the NSValue).

    As for NSInvocation, obviously you can use NSInvocation to pass
    arguments of pretty much any type you like.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • --- Alastair Houghton <alastair...>
    wrote:

    > On 25 Sep 2007, at 16:26, Dominik Pich wrote:
    >
    >> Hi, you verfied this? I was under the impressions
    > that
    >> performSelector same as NSInvocation transparently
    > 'unwraps'
    >> NSValues........or maybe only NSInvocation!?
    >
    > I think you're imagining things here.  Why would -
    > performSelector:withObject: ever want to do that?
    > For a start, you
    > wouldn't be able to pass an NSValue.

    I don't see why not. It could behave the same as KVC,
    which works both with scalars and NSValues. The only
    situation I can think of where it would create trouble
    is one where you have a method that takes an integer
    argument that is supposed to represent the address of
    an NSValue object -- I'd say that is decidedly an edge
    case.

    Cheers,
    Chuck


    ____________________________________________________________________________________
    Take the Internet to Go: Yahoo!Go puts the Internet in your pocket: mail, news, photos & more.
    http://mobile.yahoo.com/go?refer=1GNXIC
  • On 25 Sep 2007, at 19:45, Charles Steinman wrote:

    > --- Alastair Houghton <alastair...>
    > wrote:
    >
    >> On 25 Sep 2007, at 16:26, Dominik Pich wrote:
    >>
    >>> Hi, you verfied this? I was under the impressions
    >> that
    >>> performSelector same as NSInvocation transparently
    >> 'unwraps'
    >>> NSValues........or maybe only NSInvocation!?
    >>
    >> I think you're imagining things here.  Why would -
    >> performSelector:withObject: ever want to do that?
    >> For a start, you
    >> wouldn't be able to pass an NSValue.
    >
    > I don't see why not. It could behave the same as KVC,
    > which works both with scalars and NSValues.

    And if you wanted to send an NSValue *object* as an argument of a
    message?

    Besides, as I said, it's called performSelector:with**Object**:, not
    performSelector:withObjectOrMaybeSomethingElseStoredInAnNSValue: :-)

    (OK, I'm exaggerating for effect, but the point is that the behaviour
    you're describing is not performing a selector with an object, it's
    doing something quite different.)

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • > As for NSInvocation, obviously you can use NSInvocation to pass
    > arguments of pretty much any type you like.

    As long as they're 8 bytes or smaller? And yes, I think that means they
    won't work with NSRects either. I ran into this with some C++ integration;
    solution was to pass pointers instead of values...

    --
    Scott Ribe
    <scott_ribe...>
    http://www.killerbytes.com/
    (303) 722-0567 voice
  • On 26/09/2007, at 11:52 AM, Scott Ribe wrote:

    >> As for NSInvocation, obviously you can use NSInvocation to pass
    >> arguments of pretty much any type you like.
    >
    > As long as they're 8 bytes or smaller? And yes, I think that means
    > they
    > won't work with NSRects either. I ran into this with some C++
    > integration;
    > solution was to pass pointers instead of values...

    Unless you've encountered a bug in your specific usage, NSInvocation
    should work fine with passing parameters that are more than 8 bytes
    in size. It certainly works fine with a simple test program I just
    tried on my Intel Mac.

    - Chris
  • On 26 Sep 2007, at 03:29, Chris Suter wrote:

    > On 26/09/2007, at 11:52 AM, Scott Ribe wrote:
    >
    >>> As for NSInvocation, obviously you can use NSInvocation to pass
    >>> arguments of pretty much any type you like.
    >>
    >> As long as they're 8 bytes or smaller? And yes, I think that means
    >> they
    >> won't work with NSRects either. I ran into this with some C++
    >> integration;
    >> solution was to pass pointers instead of values...
    >
    > Unless you've encountered a bug in your specific usage,
    > NSInvocation should work fine with passing parameters that are more
    > than 8 bytes in size. It certainly works fine with a simple test
    > program I just tried on my Intel Mac.

    I think it's possible to cause yourself problems in C++ because of
    features like copy constructors and destructors.  NSInvocation takes
    a binary copy of the parameters... it won't call the copy constructor
    and it won't call a destructor when it's done with the copy either.
    And I imagine the runtime does another binary copy when you invoke a
    method using an NSInvocation (again, no copy constructor call, no
    destructor call afterwards).  My guess is that that's what Scott saw
    when using C++.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On Tue, 25 Sep 2007 07:08:43 -0700 (PDT), Erik Buck
    <erik.buck...> said:
    > The conceptual documentation for NSView contains the following note:
    >
    http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaViewsGuide/S
    ubcla

    ssingNSView/chapter_5_section_3.html
    >
    > "Note: The implementation of the NSView class before Mac OS X v10.4.3 could
    discard any rectangles marked as needing display within a subclass's
    implementation of drawRect:. For maximum compatibility, when marking areas as
    requiring display from within the drawRect: method it is best to call the view's
    setNeedsDisplayInRect: method using the NSObject instance method
    performSelector:withObject:afterDelay:."
    >
    > Um, isn't this supremely bad advice ? performSelector:withObject:afterDelay:.
    requires an _object_ argument.  How exactly is an NSRect structure going to be
    passed as the argument to performSelector:withObject:afterDelay:

    The docs are not necessarily telling you to use setNeedsDisplayInRect as the
    selector of performSelector. They are telling to use delayed performance as
    a way of generating the setNeedsDisplayInRect call. So, for example
    (pseudo-code follows):

    - (void) drawRect: ... {
      ...
      [self performSelector:@selector(setUpMoreRects:)
    withObject:somethingOrOther afterDelay:0.1];
      ...
    }

    - (void) setUpMoreRects: (id) somethingOrOther {
      ... // maybe decode somethingOrOther if that's where the rects are
      [self setNeedDisplayInRect:...];
    }

    m.
    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    One of the 2007 MacTech Top 25: <http://tinyurl.com/2rh4pf>
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
  • Am 26.09.2007 um 03:52 schrieb Scott Ribe:
    >> As for NSInvocation, obviously you can use NSInvocation to pass
    >> arguments of pretty much any type you like.
    >
    > As long as they're 8 bytes or smaller? And yes, I think that means
    > they
    > won't work with NSRects either. I ran into this with some C++
    > integration;
    > solution was to pass pointers instead of values...

      That's not a limitation of NSInvocation. That's how the ABI works
    under the hood: structs 8 bytes or smaller get pushed on the stack
    directly, while everything else is passed by reference. See

    <http://developer.apple.com/documentation/DeveloperTools/Conceptual/
    LowLevelABI/Articles/IA32.html
    >

    for all the gory details.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today