style question: when to use 'id' as return value?

  • Hi all,

    NSString has the method:

    + (id)stringWithString:(NSString *)aString

    Why is the return value not an 'NSString*' instead of an 'id'?  Is this
    just a stylistic preference, or...?

    Thanks,

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On 18/12/2007, at 9:14 AM, Sean McBride wrote:

    > Hi all,
    >
    > NSString has the method:
    >
    > + (id)stringWithString:(NSString *)aString
    >
    > Why is the return value not an 'NSString*' instead of an 'id'?  Is
    > this
    > just a stylistic preference, or...?
    >
    > Thanks,

    It believe it's to do with the fact that it returns an NSMutableString
    for NSMutableString. If it was just NSString you'd get a compiler
    warning for this:

      NSMutableString *s = [NSMutableString stringWithString:@"MyString"];

    - Chris
  • On Dec 17, 2007, at 2:14 PM, Sean McBride wrote:
    > NSString has the method:
    >
    > + (id)stringWithString:(NSString *)aString
    >
    > Why is the return value not an 'NSString*' instead of an 'id'?  Is
    > this
    > just a stylistic preference, or...?

    Because both NSString and NSMutableString Do The Right Thing when that
    method is invoked and, thus, declaring that the method returns a
    particular type of string -- mutable or immutable -- would be wrong
    half the time.

    b.bum
  • On 18/12/2007, at 9:25 AM, Bill Bumgarner wrote:

    > On Dec 17, 2007, at 2:14 PM, Sean McBride wrote:
    >> NSString has the method:
    >>
    >> + (id)stringWithString:(NSString *)aString
    >>
    >> Why is the return value not an 'NSString*' instead of an 'id'?  Is
    >> this
    >> just a stylistic preference, or...?
    >
    > Because both NSString and NSMutableString Do The Right Thing when
    > that method is invoked and, thus, declaring that the method returns
    > a particular type of string -- mutable or immutable -- would be
    > wrong half the time.

    NSString is a superclass of NSMutableString so it wouldn't be wrong to
    say it returns NSString.

    The real reason it returns id is for convenience: so that you don't
    have to cast to get rid of the compiler warning.

    - Chris
  • On 12/17/07 2:25 PM, Bill Bumgarner said:

    > On Dec 17, 2007, at 2:14 PM, Sean McBride wrote:
    >> NSString has the method:
    >>
    >> + (id)stringWithString:(NSString *)aString
    >>
    >> Why is the return value not an 'NSString*' instead of an 'id'?  Is
    >> this
    >> just a stylistic preference, or...?
    >
    > Because both NSString and NSMutableString Do The Right Thing when that
    > method is invoked and, thus, declaring that the method returns a
    > particular type of string -- mutable or immutable -- would be wrong
    > half the time.

    I see; makes sense, thanks!  But this was just one example, there are
    other situations where I don't understand how 'id' was chosen over a
    concrete type.  For example:

    + (id)sharedGlyphGenerator

    vs

    + (ABAddressBook *)sharedAddressBook

    Likewise, why does NSWindow's -windowController return 'id' and not
    'NSWindowController*'?

    I'm trying to understand in general...

    Cheers,

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On 18/12/2007, at 9:36 AM, Sean McBride wrote:

    > Likewise, why does NSWindow's -windowController return 'id' and not
    > 'NSWindowController*'?

    I imagine that NSWindow's windowController returns id for the same
    reason that stringFromString: does—because it often returns a subclass
    of NSWindowController and therefore it returns id so that you don't
    have to cast.

    - Chris
  • On Monday, December 17, 2007, at 02:33PM, "Chris Suter" <chris...> wrote:
    >
    > On 18/12/2007, at 9:25 AM, Bill Bumgarner wrote:
    >
    >> On Dec 17, 2007, at 2:14 PM, Sean McBride wrote:
    >>> NSString has the method:
    >>>
    >>> + (id)stringWithString:(NSString *)aString
    >>>
    >>> Why is the return value not an 'NSString*' instead of an 'id'?  Is
    >>> this
    >>> just a stylistic preference, or...?
    >>
    >> Because both NSString and NSMutableString Do The Right Thing when
    >> that method is invoked and, thus, declaring that the method returns
    >> a particular type of string -- mutable or immutable -- would be
    >> wrong half the time.
    >
    > NSString is a superclass of NSMutableString so it wouldn't be wrong to
    > say it returns NSString.

    Wouldn't be wrong, but it would be misleading, inconvenient and make an error prone pattern a too convenient habit.

    Not that (id) isn't without its problems...

    b.bum
  • On Dec 17, 2007, at 2:36 PM, Sean McBride wrote:

    > I see; makes sense, thanks!  But this was just one example, there are
    > other situations where I don't understand how 'id' was chosen over a
    > concrete type.  For example:
    >
    > + (id)sharedGlyphGenerator
    >
    > vs
    >
    > + (ABAddressBook *)sharedAddressBook
    >
    > Likewise, why does NSWindow's -windowController return 'id' and not
    > 'NSWindowController*'?
    >
    > I'm trying to understand in general...

    I would use this pattern:

    * Returning properties of an instance ("-windowController") or of a
    class ("+sharedAddressBook")
      -> Use the actual type

    * Returning new instances from an init method ("-initWithString:"), or
    a class factory method ("+stringWithString:")
      -> Use "id"

    (But as you have noted, Cocoa apparently doesn't agree with me
    completely)

    There probably should be some official documentation and guidelines
    for this, but I don't think there currently is?

    j o a r
  • On Dec 17, 2007, at 4:46 PM, William Bumgarner wrote:

    >
    > On Monday, December 17, 2007, at 02:33PM, "Chris Suter" <chris...>
    >> wrote:
    >>
    >> On 18/12/2007, at 9:25 AM, Bill Bumgarner wrote:
    >>
    >>> On Dec 17, 2007, at 2:14 PM, Sean McBride wrote:
    >>>> NSString has the method:
    >>>>
    >>>> + (id)stringWithString:(NSString *)aString
    >>>>
    >>>> Why is the return value not an 'NSString*' instead of an 'id'?  Is
    >>>> this
    >>>> just a stylistic preference, or...?
    >>>
    >>> Because both NSString and NSMutableString Do The Right Thing when
    >>> that method is invoked and, thus, declaring that the method returns
    >>> a particular type of string -- mutable or immutable -- would be
    >>> wrong half the time.
    >>
    >> NSString is a superclass of NSMutableString so it wouldn't be wrong
    >> to
    >> say it returns NSString.
    >
    > Wouldn't be wrong, but it would be misleading, inconvenient and make
    > an error prone pattern a too convenient habit.
    >
    > Not that (id) isn't without its problems...

    Maybe we need something like

    @interface NSString
    + (@class) stringWithString: (NSString *) aString;
    @end

    or some other way to indicate to the compiler that the return type is
    an instance of the class that is the receiver (or the same class, or a
    subclass, as the receiver for instance methods)?  Hopefully less ugly
    than (@class), though...

    This would also help clean up

    @interface NSObject
    + (@class) alloc;
    - (@class) init;
    - (@class) autorelease;
    @end

    So we'd know that the return value is the same subclass of NSObject
    (or is a subclass of in the case of a class cluster, but that would be
    implementation detail).

    This would catch things like

    - (Foo *) doSomething
    {
    return [[[Bar alloc] init] autorelease];
    }

    Since the compiler would know that [Bar alloc] returns an instance of
    Bar, init will also return an instance of Bar, as will autorelease,
    and if Bar isn't a subclass of Foo, it's an error.

    It would also help to avoid warnings (and the chance to do the wrong
    thing if they are ignored) with things like

    @interface Foo: NSObject
    - (id) initWithLength: (int) length;
    @end

    @interface Bar: NSObject
    - (id) initWithLength: (float) length;
    @end

    Bar *b = [[Bar alloc] initWithLength: 5]; // if we pick the wrong
    signature, the floating point value is garbage

    Glenn Andreas                      <gandreas...>
    <http://www.gandreas.com/> wicked fun!
    quadrium | prime : build, mutate, evolve, animate : the next
    generation of fractal art
previous month december 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
31            
Go to today