Convenience Methods

  • 2 quick questions for the guru's here.

    1)  I've added a convenience method to a class of mine so that it
    minimizes the init/retains needed to create one just to slap into an
    array.  My question is, in the method should I be returning an object
    of type (id) or an object that is the type of the class I'm
    returning?  Example:

    + (id)repoWithName:(NSString *)name OfType:(SVNDatabases)type AtPath:
    (NSString *)path
    {
    id newInstance = [[[self class] alloc] init];

    [newInstance setName: name];
    [newInstance setType: type];
    [newInstance setPath: path];

    [newInstance loadUsers];

    return [newInstance autorelease];
    }

    2) Using this method the object is autoreleased at some later point.
    If I add this to an array/dictionary etc....does the dictionary
    automaticly retain an instance or will it still disappear later?
    Example:

    [repositories setObject: [SVNRepository repoWithName: repoName
    OfType: SVNFSFS AtPath: repoPath] forKey: repoName];

    Thanks.

    ------------------
    Dawn's cold passion filters through the trees
    In darkness I showed you ecstacy
    In darkness I took you from this world
    Light shall never fall upon your face again

    From spring I've waited for this winter's reap
    I've watched you pass autumn's twilight yearning

    Now with dawn I bring you eternal sleep
    And silence all forever mourning...
  • On Sep 25, 2007, at 7:31 PM, Tim Davis wrote:

    > 1)  I've added a convenience method to a class of mine so that it
    > minimizes the init/retains needed to create one just to slap into
    > an array.  My question is, in the method should I be returning an
    > object of type (id) or an object that is the type of the class I'm
    > returning?

    You should always return an object of type id, so that the class
    method will work correctly with subclasses of the object.

    > 2) Using this method the object is autoreleased at some later
    > point.  If I add this to an array/dictionary etc....does the
    > dictionary automaticly retain an instance or will it still
    > disappear later?

    Arrays, dictionaries, and sets all retain their objects when they are
    added.

    Nick Zitzmann
    <http://www.chronosnet.com/>
  • >> 1)  I've added a convenience method to a class of mine so that it
    >> minimizes the init/retains needed to create one just to slap into
    >> an array.  My question is, in the method should I be returning an
    >> object of type (id) or an object that is the type of the class I'm
    >> returning?
    >
    > You should always return an object of type id, so that the class
    > method will work correctly with subclasses of the object.

    Sorry, I don't understand this.

    There is no way the class method is going to return subclasses.

    If you mean, so you can overload the class method in subclasses, then they
    can still return the superclass since anything they create will be valid
    instance of the superclass.  They'll be a valid instance of the subclass as
    well.

    (Typically, you wouldn't implement the same named convenience in a subclass
    anyway - the name of the convenience is typically derived from the class it
    is returning/is a part of)

    Personally, I think too much use of 'id' subverts the compilers natural
    ability to detect missing method definitions, etc.
  • On Sep 25, 2007, at 7:46 PM, Jeff Laing wrote:

    > Sorry, I don't understand this.
    >
    > There is no way the class method is going to return subclasses.

    Really? Don't tell that to whoever designed NSString (for example)
    and its subclasses. Because they return id and not a specific
    instance of the class, you can call [NSMutableString
    stringWithString:@"hello world"] and get a mutable string instead of
    a regular string.

    Nick Zitzmann
    <http://www.chronosnet.com/>
  • On Sep 25, 2007, at 6:31 PM, Tim Davis wrote:

    > 2) Using this method the object is autoreleased at some later
    > point.  If I add this to an array/dictionary etc....does the
    > dictionary automaticly retain an instance or will it still disappear
    > later?  Example:
    >
    This is described in the documentation: <http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articl
    es/mmPractical.html
    >

    mmalc
  • > On Sep 25, 2007, at 7:46 PM, Jeff Laing wrote:
    >
    >> Sorry, I don't understand this.
    >>
    >> There is no way the class method is going to return subclasses.
    >
    > Really? Don't tell that to whoever designed NSString (for example)
    > and its subclasses. Because they return id and not a specific

    You are confusing the issue.

    The original posters convenience method is not going to return subclasses,
    so when he asks about what type to return, suggestions should be applicable
    to his code.

    NSString's convenience methods are hiding class clusters, and I stand by the
    assertion that every one of them returns a valid NSString object.  They may
    be subclasses of NSString but that does not make them non-NSString objects.
    Inheritance 101.
  • On Sep 25, 2007, at 8:10 PM, Jeff Laing wrote:

    > NSString's convenience methods are hiding class clusters, and I
    > stand by the
    > assertion that every one of them returns a valid NSString object.
    > They may
    > be subclasses of NSString but that does not make them non-NSString
    > objects.
    > Inheritance 101.

    But class clusters are an implementation detail, and the code
    shouldn't care. What's important is +[NSMutableString
    stringWithString:] returns an NSMutableString, and +[NSString
    stringWithString:] returns an NSString, despite the fact that
    +stringWithString: is only defined by NSString.

    I know they both really return an NSCFString, but again, it's an
    implementation detail.

    Nick Zitzmann
    <http://www.chronosnet.com/>
  • On Sep 25, 2007, at 10:10 PM, Jeff Laing wrote:

    > NSString's convenience methods are hiding class clusters, and I
    > stand by the
    > assertion that every one of them returns a valid NSString object.
    > They may
    > be subclasses of NSString but that does not make them non-NSString
    > objects.
    > Inheritance 101.

    But you are still missing the point of why you'd want it to be
    declared to return id. Even though an NSMutableString is an NSString,
    you want to be able to write

    NSMutableString *string = [NSMutableString stringWithXXX: ...];

    If the method were declared to return NSString *, the compiler would
    warn you about an assignment from a distinct Objective-C type.

    Jim
  • On Sep 25, 2007, at 19:15, Nick Zitzmann wrote:

    >
    > On Sep 25, 2007, at 8:10 PM, Jeff Laing wrote:
    >
    >> NSString's convenience methods are hiding class clusters, and I
    >> stand by the
    >> assertion that every one of them returns a valid NSString object.
    >> They may
    >> be subclasses of NSString but that does not make them non-NSString
    >> objects.
    >> Inheritance 101.
    >
    > But class clusters are an implementation detail, and the code
    > shouldn't care. What's important is +[NSMutableString
    > stringWithString:] returns an NSMutableString, and +[NSString
    > stringWithString:] returns an NSString, despite the fact that
    > +stringWithString: is only defined by NSString.

    Another good example is NSCharacterSet on 10.4 and earlier.  The
    following code causes a warning of "initialization from distinct
    Objective-C type":

    NSMutableCharacterSet *cset = [NSMutableCharacterSet
    characterSetWithCharactersInString:@"test"];

    because +[NSCharacterSet characterSetWithCharactersInString:] is typed
    to return an NSCharacterSet instead of id.  This is annoying (and
    wrong), because it really is a mutable character set.

    --
    Adam
  • On Sep 25, 2007, at 9:33 PM, Adam R. Maxwell wrote:

    >
    > On Sep 25, 2007, at 19:15, Nick Zitzmann wrote:
    >
    >>
    >> On Sep 25, 2007, at 8:10 PM, Jeff Laing wrote:
    >>
    >>> NSString's convenience methods are hiding class clusters, and I
    >>> stand by the
    >>> assertion that every one of them returns a valid NSString
    >>> object.  They may
    >>> be subclasses of NSString but that does not make them non-
    >>> NSString objects.
    >>> Inheritance 101.
    >>
    >> But class clusters are an implementation detail, and the code
    >> shouldn't care. What's important is +[NSMutableString
    >> stringWithString:] returns an NSMutableString, and +[NSString
    >> stringWithString:] returns an NSString, despite the fact that
    >> +stringWithString: is only defined by NSString.
    >
    > Another good example is NSCharacterSet on 10.4 and earlier.  The
    > following code causes a warning of "initialization from distinct
    > Objective-C type":
    >
    > NSMutableCharacterSet *cset = [NSMutableCharacterSet
    > characterSetWithCharactersInString:@"test"];
    >
    > because +[NSCharacterSet characterSetWithCharactersInString:] is
    > typed to return an NSCharacterSet instead of id.  This is annoying
    > (and wrong), because it really is a mutable character set.

    OTOH (unless this was changed from when I ran head first into it),
    doing:

    NSMutableParagraphStyle *style = [NSMutableParagraphStyle
    defaultParagraphStyle];

    also give you a warning which, in fact, is correct (since this
    returns an immutable NSParagraphStyle, not a mutable one that you
    might expect).

    Proof that not everything in Cocoa is perfectly consistent everywhere
    (though I find the inconsistent usage of [-NSDocument fileName] vs [-
    NSSavePanel filename] more annoying).

    Glenn Andreas                      <gandreas...>
      <http://www.gandreas.com/> wicked fun!
    quadrium2 | build, mutate, evolve, animate  | images, textures,
    fractals, art
  • On 26/09/2007, at 11:46 AM, Jeff Laing wrote:

    > There is no way the class method is going to return subclasses.

    Yes there is.

    If you call [MySubclass repoWithName:…] you'd get an instance of
    MySubclass. You don't need to override the class method for it to do
    this.

    And with regards to the original question, you shouldn't use "id" as
    the return type. The class cluster issue mentioned in other posts is
    confusing the issue. NSString methods are declared as returning
    NSString * not id. This is because you want the compiler to know as
    much about the type as possible i.e. that the object returned
    conforms to all the methods declared by NSString. The fact that it's
    a different class underlying this doesn't matter — the object
    returned conforms to NSString.

    - Chris
  • > But class clusters are an implementation detail, and the code
    > shouldn't care. What's important is +[NSMutableString

    Once again, you've confused things.

    He's writing "the code".  Not calling it.  Writing it.  He has to take
    implementation detail into account and thats what he was asking - how should
    he implement this, return 'id' or 'myclass*'

    > shouldn't care. What's important is +[NSMutableString
    > stringWithString:] returns an NSMutableString, and +[NSString
    > stringWithString:] returns an NSString, despite the fact that
    > +stringWithString: is only defined by NSString.

    Now, when I look at the online docs for NSMutableString, I see that it
    doesn't actually define stringWithString: as a class method.

    http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes
    /

    NSMutableString_Class/Reference/Reference.html

    so what you have is an indeterminate API. The docs for NSString
    stringWithString: say

    "stringWithString:

    Returns a string created by copying the characters from another given
    string."

    which isn't guaranteed to be true, is it?  There is no need for it to "copy"
    bytes from that other string - thats the whole point of class clusters, that
    they can optimise away unneeded operations.  I would expect that the
    implementation of [NSString stringWithString:] would look at what it was
    copying and just add a reference to the data bytes, if they were
    non-mutable.

    And it doesn't say 'returns a mutable string' so irrespective of what
    [NSMutableString stringWithString:] returns, you should not be treating it
    as though it were mutable.  This is where the whole 'mutable' thing falls
    down. In my opinion (and thats what this is, an opinion), it is better to
    explicitly declare return types rather than fall back on returning id, in
    the general case, irrespective of the confusion that class clusters bring to
    the issue.  The fact that Apple (and presumably Next) didn't do it sensibly
    is irrelevant to my opinion.

    The compiler doesn't catch me doing something stupid like:

    NSMutableString *s = [NSString
    stringWithString:iHaveNoIdeaIfThisWasMutable];

    because its completely legal.  I need to wait till runtime, and many lines
    later in my source, to discover that I made an error that an explicit
    message prototype would have found at compile time.  In terms of
    productivity of programmer, there's no comparison.
  • >> There is no way the class method is going to return subclasses.
    >
    > Yes there is.

    No there isn't.

    > If you call [MySubclass repoWithName:…] you'd get an instance of
    > MySubclass. You don't need to override the class method for it to do
    > this.

    Yes, you called some other class method, not the one being discussed.

    > And with regards to the original question, you shouldn't use "id" as
    > the return type. The class cluster issue mentioned in other posts is
    > confusing the issue. NSString methods are declared as returning
    > NSString * not id.

    Not according to
    http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes
    /

    NSMutableString_Class/Reference/Reference.html

    stringWithCapacity:

    Returns an empty NSMutableString object with initial storage for a given
    number of characters.

    + (id)stringWithCapacity:(unsigned)capacity

    etc.
  • > But you are still missing the point of why you'd want it to be
    > declared to return id. Even though an NSMutableString is an
    > NSString,
    > you want to be able to write
    >
    > NSMutableString *string = [NSMutableString stringWithXXX: ...];
    >
    > If the method were declared to return NSString *, the compiler would
    > warn you about an assignment from a distinct Objective-C type.

    I understand exactly what you are saying.

    But as far as inheritance contracts go out in OO land, I would *expect* that

    [NSMutableString stringWithXXX: ...]

    would return a *non*-mutable string since thats the contract that its
    superclass establishes, and that NSMutableString does not redefine, at least
    not that I can see..  Except that NSString does *not* establish that
    contract, it cops out and returns 'id' - a clumsy implementation detail
    hiding out in interface land.

    The problem here is with the 'isa' part of inheritance.  The definition of
    NSString is 'a datatype that cannot be edited'.  How can NSMutableString be
    a *subclass* of that definition?  In my opinion, the method I call should
    be:

    [NSMutableString mutableStringWithXXX: ...]

    because that makes it clear that I want the method defined in
    NSMutableString, not the inherited one from NSString whose implicit
    interface contract only returns non-mutable objects.

    We're really straying well into "well tough, thats how it was defined" land
    and I understand that.  But just because poor choices were made when setting
    up NSString, that does not mean that its good form to propagate them into
    your own code, especially since no-one ever sets up mutable and immutable
    variants of their own classes.  The whole 'should I return id or myclass*'
    discussion really does hinge on that, in my opinion.

    Early on, I wondered why 'const' never gets used in Objective-C.  Presumably
    because even an operation that would be const in other systems (adding it to
    an array) results in the object itself having its retain count incremented
    (which is an implementation detail of the array, not the object).  The
    mutable/immutable differentiation strikes me as a clumsy attempt to resolve
    that problem, but it doesn't get there.  In my opinion.  Which may well
    differ from yours.
  • There are at least two opposing schools of thought
    about the value vs. cost of static compile time type
    checking.

    One school says that compilers should protect
    programmers and make them more productive by detecting
    as many errors at compile time as possible.  If the
    programmer is forced into monkey puzzles with the type
    system or has to add extra lines of code to work
    around the type system (Ref: the "Command" pattern),
    that's a small price to pay for type safety.

    The other school, founded with Small-talk and
    continued with languages like Python, claims that
    maximum code re-use and minimal code size makes
    programmers much more productive than any type
    systems.  Small-talk would say that there is only one
    type, object, and all objects are interchangeable.
    Surely, adequate testing will catch any run-time error
    that a static type system might have caught and more.

    My recollection is that long ago Objective-C used to
    rely much more on the id type and there was not static
    type checking for methods.  NeXT and Apple clearly
    voted for more type safety without quite going so far
    and forbidding dynamic typing.  Maybe the middle
    ground isn't ideal in this case ?

    References:
    http://www.mindview.net/WebLog/log-0025
    http://www.cocoabuilder.com/archive/message/cocoa/2007/5/13/183149
  • I'm a little hesitant to throw my bit into this conversation, since I
    don't see it going to any logical conclusion other than off-topic and
    then moderated.  Nonetheless, I must say that:

    > [NSMutableString mutableStringWithXXX: ...]

    There's redundancy there.  I've indicated the desired mutability by
    specifying that I want an NSMutableString; I don't need to say any
    more.  When I read "stringWithXXX" I read it as "creates a 'string'
    with XXX", where 'string' is just a generic usage of the term.  An
    NSString is a string.  An NSMutableString is a string.  An
    NSAttributedString is a string (although that's where Foundation's
    class hierarchy goes wonko).

    Personally, I like strong typing, so I avoid "id" unless I really
    mean "I don't know and/or care about the type of this object".
    Having it there is a huge boon to problem solving, but it's there for
    specific reasons, not so you can be lazy.  IMHO.

    Wade
  • On 26 Sep 2007, at 03:59, Jeff Laing wrote:

    >>> There is no way the class method is going to return subclasses.
    >>
    >> Yes there is.
    >
    > No there isn't.

    Actually it depends.

    >> If you call [MySubclass repoWithName:…] you'd get an instance of
    >> MySubclass. You don't need to override the class method for it to do
    >> this.
    >
    > Yes, you called some other class method, not the one being discussed.

    It depends on whether the method does

        [[self alloc] initWithName:...]

    or

        [[MyClass alloc] initWithName:...]

    In the former case, you'd get an instance of the subclass, in the
    latter of the original class.  Both styles are in use in Cocoa programs.

    But, nevertheless, Jeff is right (in his other posts) that a subclass
    is also a valid instance of its superclass, so in a sense this is
    (sometimes) irrelevant.  Also, I don't think the compiler checks that
    the method signatures are identical (unlike in C++, where this is a
    requirement), so you can probably just declare the class method
    differently in your subclass.  This makes sense, because (after all)
    you're normally writing

          [MyClass repoWithName:@"Foo"]
      or [MySubclass repoWithName:@"Foo"]

    rather than

      [someClass repoWithName:@"Foo"]

    (which I'd probably write in full as [[someClass alloc]
    initWithName:@"Foo"]).

    Another downside of using id is that it means that the compiler
    sometimes has to guess which method you mean.  For instance, if you
    make a method called -size that returns (e.g.) a size in bytes, and
    you call it via an id, you'll probably find that the compiler thinks
    it should return an NSSize and not whatever your actual method
    returns.  That's because it doesn't know the type, so it looks
    through the messages it knows about and the first one it finds called
    "size" happens not to be the one you declared.  (I've had this happen
    on a couple of occasions... I'm sure I even managed to cause a bug
    somewhere because of this "guessing" behaviour, though I don't
    remember the details).

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On Sep 25, 2007, at 11:15 PM, Jeff Laing wrote:

    > But as far as inheritance contracts go out in OO land, I would
    > *expect* that
    >
    > [NSMutableString stringWithXXX: ...]
    >
    > would return a *non*-mutable string since thats the contract that its
    > superclass establishes, and that NSMutableString does not redefine,
    > at least
    > not that I can see..  Except that NSString does *not* establish that
    > contract

    The object mutability guide mentions that <type>With<Type> methods
    are available for converting between mutable and immutable objects.
    But beyond the examples given, it doesn't enumerate them.

    > The problem here is with the 'isa' part of inheritance.  The
    > definition of
    > NSString is 'a datatype that cannot be edited'.  How can
    > NSMutableString be
    > a *subclass* of that definition?

    When you are controlling the definitions, it is easy to set up a
    straw man then knock him down :-).

    If you define an NSString as an object which is conceptually an array
    of unicode characters, and define NSMutableString as a subclass which
    extends the capabilities to include modifying the stored characters,
    it doesn't sound nearly as distasteful.

    (I know what the current documentation says, and am I am ignoring it
    intentionally here.)

    > Early on, I wondered why 'const' never gets used in Objective-C.
    > Presumably
    > because even an operation that would be const in other systems
    > (adding it to
    > an array) results in the object itself having its retain count
    > incremented
    > (which is an implementation detail of the array, not the object).

    C++ has the mutable keyword so that you can, for example, alter the
    reference count in an otherwise const situation.

    > The mutable/immutable differentiation strikes me as a clumsy
    > attempt to resolve
    > that problem, but it doesn't get there.  In my opinion.  Which may
    > well
    > differ from yours.

    It goes much deeper than applying a const modifier to say this method
    shall not mutate this object. (And that is ignoring the runtime
    dispatching of Objective-C.) It is saying that this object will not
    be mutated, ever, no matter what messages are sent to it. Sprinkling
    const here and there doesn't solve that problem, because anyone who
    gets a reference to your object *could* mutate it, if it were mutable.

    The object mutability reference discusses some of the reasons why the
    Cocoa frameworks have mutable and immutable variants of objects.

    This is not an uncommon approach which you'll find used in other
    domains (Java, Python, etc.)

    Jim
  • On Sep 26, 2007, at 7:03 AM, Alastair Houghton wrote:

    > On 26 Sep 2007, at 03:59, Jeff Laing wrote:
    >
    >>>> There is no way the class method is going to return subclasses.
    >>>
    >>> Yes there is.
    >>
    >> No there isn't.
    >
    > Actually it depends.
    >
    >>> If you call [MySubclass repoWithName:…] you'd get an instance of
    >>> MySubclass. You don't need to override the class method for it to do
    >>> this.
    >>
    >> Yes, you called some other class method, not the one being discussed.
    >
    > It depends on whether the method does
    >
    > [[self alloc] initWithName:...]
    >
    > or
    >
    > [[MyClass alloc] initWithName:...]
    >
    > In the former case, you'd get an instance of the subclass, in the
    > latter of the original class.  Both styles are in use in Cocoa
    > programs.
    >
    > But, nevertheless, Jeff is right (in his other posts) that a
    > subclass is also a valid instance of its superclass, so in a sense
    > this is (sometimes) irrelevant.  Also, I don't think the compiler
    > checks that the method signatures are identical (unlike in C++,
    > where this is a requirement), so you can probably just declare the
    > class method differently in your subclass.  This makes sense,
    > because (after all) you're normally writing
    >
    > [MyClass repoWithName:@"Foo"]
    > or [MySubclass repoWithName:@"Foo"]
    >
    > rather than
    >
    > [someClass repoWithName:@"Foo"]
    >
    > (which I'd probably write in full as [[someClass alloc]
    > initWithName:@"Foo"]).
    >
    > Another downside of using id is that it means that the compiler
    > sometimes has to guess which method you mean.  For instance, if you
    > make a method called -size that returns (e.g.) a size in bytes, and
    > you call it via an id, you'll probably find that the compiler
    > thinks it should return an NSSize and not whatever your actual
    > method returns.  That's because it doesn't know the type, so it
    > looks through the messages it knows about and the first one it
    > finds called "size" happens not to be the one you declared.  (I've
    > had this happen on a couple of occasions... I'm sure I even managed
    > to cause a bug somewhere because of this "guessing" behaviour,
    > though I don't remember the details).

    The above paragraph is exactly why I posed this question in the first
    place.  The class overrides a couple properties and the compiler
    generates warnings about which one is being called.  So far nothing
    is working improperly, but I want to make sure that later down the
    line I'm not getting weird nondescript bugs that I can't find because
    of this.

    And it's being init'd like this:

    id newInstance = [[[self class] alloc] init];

      So should it be:

    MyClass newInstance = [[MyClass alloc] init];

    ???

    >
    > Kind regards,
    >
    > Alastair.
    >
    > --
    > http://alastairs-place.net
    >
    >
  • On 26 Sep 2007, at 13:39, Tim Davis wrote:

    > On Sep 26, 2007, at 7:03 AM, Alastair Houghton wrote:
    >
    >> Another downside of using id is that it means that the compiler
    >> sometimes has to guess which method you mean.  For instance, if
    >> you make a method called -size that returns (e.g.) a size in
    >> bytes, and you call it via an id, you'll probably find that the
    >> compiler thinks it should return an NSSize and not whatever your
    >> actual method returns.  That's because it doesn't know the type,
    >> so it looks through the messages it knows about and the first one
    >> it finds called "size" happens not to be the one you declared.
    >> (I've had this happen on a couple of occasions... I'm sure I even
    >> managed to cause a bug somewhere because of this "guessing"
    >> behaviour, though I don't remember the details).
    >
    > The above paragraph is exactly why I posed this question in the
    > first place.  The class overrides a couple properties and the
    > compiler generates warnings about which one is being called.  So
    > far nothing is working improperly, but I want to make sure that
    > later down the line I'm not getting weird nondescript bugs that I
    > can't find because of this.
    >
    > And it's being init'd like this:
    >
    > id newInstance = [[[self class] alloc] init];
    >
    > So should it be:
    >
    > MyClass newInstance = [[MyClass alloc] init];

    Well

      MyClass *newInstance = [[MyClass alloc] init];

    is one way to do it, yes.  If you do that, then the compiler knows
    the type of object whose methods it's supposed to be invoking, so you
    shouldn't get any warnings.

    Sometimes when I'm writing convenience constructors I might write

      + (MyClass *)myClassWithFoo:(int)f
      {
          return [[[self alloc] initWithFoo:f] autorelease];
      }

    where "self" is, of course, the class.  A good reason to do that is
    that it means that a subclass only has to override -initWithFoo: in
    order to make +myClassWithFoo: work as expected.

    If you find that you have problems, you can obviously assign to a
    variable with a more specific type, or you can just cast.  For
    instance, if there happens to be some other -initWithFoo: method with
    a different parameter type that is causing problems, you could write

          return [[(MyClass *)[self alloc] initWithFoo:f] autorelease];

    instead.

    I should also mention that, thankfully, the problem of confusion of
    methods with the same name is fairly unusual, at least in my
    experience.  And when it does happen, you can easily resolve it,
    either by renaming one of the methods, or by giving the compiler more
    type information.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • Am 26.09.2007 um 03:38 schrieb Nick Zitzmann:
    > You should always return an object of type id, so that the class
    > method will work correctly with subclasses of the object.

      Just to avoid confusion:

    - It's a convention to have convenience methods declared with a
    return type of "id". I'm not sure why. If you declare your
    convenience method to return a MyObject*, and your subclass also uses
    the return type MyObject*, that's completely OK. Subclasses of
    MyObject are valid MyObjects, too.

    - You *can not* return an *object* of type id, because id is an
    abstract type. There is no way to instantiate an id. This is mainly
    just terminology nitpicking, but maybe it saves some beginner a lot
    of time confusedly trying to come up with a way to instantiate an id...

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • >> The problem here is with the 'isa' part of inheritance.  The definition
    of
    >> NSString is 'a datatype that cannot be edited'.  How can NSMutableString
    be
    >> a *subclass* of that definition?
    >
    > When you are controlling the definitions, it is easy to set up a
    > straw man then knock him down :-).
    >
    > If you define an NSString as an object which is conceptually an array
    > of unicode characters, and define NSMutableString as a subclass which
    > extends the capabilities to include modifying the stored characters,
    > it doesn't sound nearly as distasteful.

    Hey, I'm just revisiting OO-Design-102, the lecture where the prof says "who
    thinks a square is a subclass of rectangle or vice versa" and then goes on
    to ask you to justify your choice, and the mathematicians jump one way while
    the programmers jump another...

    (And, of course, NSString is *not* an array of unicode characters, thats
    implementation detail :)

    > The object mutability reference discusses some of the reasons why the
    > Cocoa frameworks have mutable and immutable variants of objects.

    That would be the document that contains the gem: "Although in theory
    immutability guarantees that an object's value is stable, in practice this
    guarantee isn't always assured". Comforting. It also contains the following:

    "When the mutability of objects is an issue, it's best to adopt some
    defensive programming practices.
    -
    -
    - Rely on the return type for indications of mutability."

    (What a shame that the NS classes don't help you out here)

    I'm seriously interested in why Apple bother having such a document - is
    there an expectation that in my (for example) iTunes-access-library, I would
    seriously consider having ITTrack and ITMutableTrack classes?  ITArtist and
    ITMutableArtist?

    Its a serious question.  Who would include mutable and immutable classes in
    the own application?  Even in their own frameworks?  Why doesn't the
    AddressBook.framework provide ABPerson and ABMutablePerson?
  • > Sometimes when I'm writing convenience constructors I might write
    >
    > + (MyClass *)myClassWithFoo:(int)f
    > {
    > return [[[self alloc] initWithFoo:f] autorelease];
    > }
    >
    > where "self" is, of course, the class.  A good reason to do that is
    > that it means that a subclass only has to override -initWithFoo: in
    > order to make +myClassWithFoo: work as expected.

    I'm still undecided on whether this is a good thing or not.  I'm going to go
    one level more specific.

    @class Animal
    + (Animal*)animalWithName:(NSString*)n    { ... }
    - (Animal*)initWithName:(NSString*)n    { ... }
    @end

    @class Dog(Animal)
    @end

    Now, in this case, you can call

    Dog *pet = [Dog animalWithName:@"Rover"];

    but that feel uncomfortable to me.  I'd still be inclined to add another
    method so I could write:

    Dog *pet = [Dog dogWithName:@"Rover"];

    even if all it did was wrap up the same methods.  ie,

    + (Dog*)dogWithName:(NSString*)n
    {
        return [[[[self class] alloc] initWithName:n] autorelease];
    }

    Its clearly a subjective issue, a comfort thing.  At the very least, it
    gives me somewhere different to put a breakpoint when it all goes "to the
    dogs", so to speak.
  • >> Sometimes when I'm writing convenience constructors I might write
    >>
    >> + (MyClass *)myClassWithFoo:(int)f
    >> {
    >> return [[[self alloc] initWithFoo:f] autorelease];
    >> }
    >>
    >> where "self" is, of course, the class.  A good reason to do that is
    >> that it means that a subclass only has to override -initWithFoo: in
    >> order to make +myClassWithFoo: work as expected.
    >
    > I'm still undecided on whether this is a good thing or not.

    After my Dog's example, it occurred to me that my uneasiness was because of
    an encapsulation thing again.

    When you say "a subclass only has to override -initWithFoo:", how did you
    *know* that the superclass had done the right thing in its myClassWithFoo: ?

    Subclasses shouldn't depend on implementation detail in their superclasses,
    should they?

    If you wrote all the code then sure, you can be confident because you can
    look at it.  But speaking as someone who works on a development team that
    works on *millions* of lines of code, I've gotten a lot more paranoid about
    assuming things about "the other guy" making changes that he's sure won't
    affect anyone...
  • > @class Animal
    > + (Animal*)animalWithName:(NSString*)n    { ... }
    > - (Animal*)initWithName:(NSString*)n    { ... }
    > @end
    >
    > @class Dog(Animal)
    > @end
    >
    > Now, in this case, you can call
    >
    > Dog *pet = [Dog animalWithName:@"Rover"];
    >
    > but that feel uncomfortable to me.  I'd still be inclined to add
    > another
    > method so I could write:
    >
    > Dog *pet = [Dog dogWithName:@"Rover"];

    But you could still say [Dog animalWithName:].  A dog is still an
    animal, right?  I mean, it's subjective, sure, but the next step along
    your example is germanShephardWithName:, and it just continues
    downhill from there... generalism for teh win.

    Additionally, with the above approach you've now got two identical
    ways to create a dog, unless you do something mean like override
    animalWithName: in your subclass and break it.  And then you can't
    write very generic code, where you might have some random class that
    you know is an animal (e.g. you got it from an existing instance of an
    animal), but you don't know anything else - then you do appreciate
    having a generic method which works for all subclasses.

    I appreciate what you're getting at, and I wouldn't particularly care
    if I was using your Animals framework and there was this extra
    dogWithName: method on the Dog subclass... but I probably wouldn't use
    it.  And I think it's just dodging the larger issue, which is that
    [Dog dogWithName:], like [Animal animalWithName:], is redundant.
    Something like [Animal createWithName:] would be better (which also
    makes it explicit that you're creating a new animal...
    "animalWithName:" sounds to me like NSImage's imageNamed:, which
    returns the existing instance with that name, not a new one).

    Wade
  • There are in theory substantial advantages to using immutable objects
    and immutable collections.  One theoretical advantage of immutable
    objects is substantial optimization from implementing -copy as

    - (id)copy
    {
      return [self retain];
    }

    In theory, collection enumerators get simpler (and faster) when used
    with immutable collections.  Pass by value (which can be expensive)
    is less necessary when using immutable objects.  It is safe to
    directly return immutable instance variables...

    I don't know if NeXT or Apple ever provided truly immutable objects
    like NSString, NSArray, etc.  With the adoption of Core Foundation
    and toll free bridged classes, any theoretical advantages of true
    immutability were completely abandoned.  Without true immutability,
    it is certainly valid to wonder why Cocoa maintains the (now
    artificial) distinction between supposedly immutable classes and
    their mutable subclasses.

    Sadly, I suspect the only reason that NSMutableArray and friends
    continue to exist is so that "legacy" Cocoa code would not break.
    Nothing stops Apple from re-factoring and moving the mutation methods
    into the base classes now.  NSMutableArray and friends would just
    become vestigial framework support for "old" code.

    Alternatively, Apple could decide to create NSCFImmutableArray or
    take other steps to restore true immutability.  Apple is
    inscrutable!  From my perspective, Apple currently has the worst of
    both worlds: They have the clutter and complexity of distinctions
    between mutable and immutable classes; They don't get any benefits
    from immutable classes because they aren't really immutable.
  • > They don't get any benefits from immutable classes because they
    > aren't really immutable.

    This has been said a few times, but I can't think of any examples
    where you can mutate a NSArray, or NSString, or other such immutable
    classes.  ?

    Wade
  • On Sep 26, 2007, at 4:59 PM, Jeff Laing wrote:

    >>> Sometimes when I'm writing convenience constructors I might write
    >>>
    >>> + (MyClass *)myClassWithFoo:(int)f
    >>> {
    >>> return [[[self alloc] initWithFoo:f] autorelease];
    >>> }
    >>>
    >>> where "self" is, of course, the class.  A good reason to do that is
    >>> that it means that a subclass only has to override -initWithFoo: in
    >>> order to make +myClassWithFoo: work as expected.
    >>
    >> I'm still undecided on whether this is a good thing or not.
    >
    > After my Dog's example, it occurred to me that my uneasiness was
    > because of
    > an encapsulation thing again.
    >
    > When you say "a subclass only has to override -initWithFoo:", how
    > did you
    > *know* that the superclass had done the right thing in its
    > myClassWithFoo: ?
    >
    > Subclasses shouldn't depend on implementation detail in their
    > superclasses,
    > should they?

    This is just my opinion, but I think that YES, absolutely, subclasses
    should know and depend upon the implementation details of their
    superclasses. To assume that you don't have to know how your own
    superclass works is taking object-oriented theory too far, and just
    leads to overly defensive programming, which means more redundant
    code. Not to mention the belief that you can just ignore how your
    superclass is implemented leads to all sorts of bad code and
    complacency. If you know what your superclass is doing, you write
    less code in your subclass, and the overall effect is that you _can_
    know what your superclasses are doing because as a whole there is
    much less code to understand.

    At framework boundaries the argument gets a little different. There I
    think you need to take extra care with documentation and defensive
    programming. (By a framework boundary I mean where your superclass is
    in a different framework than the subclass. A subclass of NSView in
    your own application, for instance.)

    But certainly in your own "Animals" framework, you better know how
    your Animal superclass works, and you ought to take advantage of that
    fact to write as little code as possible.

    If you don't want to depend upon the internals of a class then you
    shouldn't be subclassing it. You should be using a Composite pattern.
    Embed an instance of the other class as an instance inside your own
    and pass along messages to it. Apple advises using this pattern
    rather than subclassing for class clusters:

    http://developer.apple.com/documentation/Cocoa/Conceptual/
    CocoaFundamentals/CocoaObjects/chapter_3_section_9.html

    Finally, the design of Objective-C and Cocoa tends to lead to fairly
    flat and broad inheritance hierarchies. There aren't that many deep
    subclasses of subclasses of subclasses like you tend to find in some
    other frameworks. Patterns like delegation keeps the number of
    subclasses you need to write yourself to a minimum. The vast majority
    of your own code ought to be a single step away from one of a few
    "hub" superclasses: NSObject, NSView, NSWindowController. That means
    you won't have to know the details of lots of superclasses above you
    - there just aren't that many to learn.

    > If you wrote all the code then sure, you can be confident because
    > you can
    > look at it.  But speaking as someone who works on a development
    > team that
    > works on *millions* of lines of code, I've gotten a lot more
    > paranoid about
    > assuming things about "the other guy" making changes that he's sure
    > won't
    > affect anyone...

    If he reimplements a superclass and doesn't check to see whether any
    subclasses were depending upon the previous implementation, I'd bitch
    at "the other guy" until he fixes it.

    These days the code base on my project is only around 300k lines. But
    I think that the exact same functionality could easily be twice as
    many lines of code if we stressed OO-orthodoxy and redundant safety
    over clean and minimal. And I have been in the several million plus
    lines situation for years at a time in my former life as a
    consultant, so I do have experience with quite large code bases.

    -- Greg
  • On Sep 26, 2007, at 8:40 PM, Wade Tregaskis wrote:

    >> They don't get any benefits from immutable classes because they
    >> aren't really immutable.
    >
    > This has been said a few times, but I can't think of any examples
    > where you can mutate a NSArray, or NSString, or other such
    > immutable classes.  ?
    >
    > Wade

    You're right!  However, you might find this test program slightly
    amusing:

    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[])
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        // Find out if CFArray (which is really NSCFArray) responds to -
    addObject:
        CFArrayRef    testArray1 = CFArrayCreate(NULL, NULL, 0, NULL);

        if([(id)testArray1 respondsToSelector:@selector(addObject:)])
        {
          NSLog(@"%@ respondsToSelector:@selector(addObject:)", [(id)
    testArray1 class]);
        }

        // Find out if NSArray (which is really NSCFArray) responds to -
    addObject:
        NSArray        *testArray2 = [NSArray array];

        if([testArray2 respondsToSelector:@selector(addObject:)])
        {
          NSLog(@"%@ respondsToSelector:@selector(addObject:)",
    [testArray2 class]);
        }

        // Find out if NSMutableArray (which is really NSCFArray)
    responds to -addObject:
        NSArray        *testArray3 = [NSMutableArray array];

        if([testArray3 respondsToSelector:@selector(addObject:)])
        {
          NSLog(@"%@ respondsToSelector:@selector(addObject:)",
    [testArray3 class]);
        }

        // Find out if copying an "immutable" array returns the sameobject
        NSLog(@"Is %@ the same as its copy: %@ ?", [(id)testArray1
    class], ((id)testArray1 == [[(id)testArray1 copy] autorelease]) ?
    @"YES" : @"NO");
        NSLog(@"Is %@ the same as its copy: %@ ?", [testArray2 class],
    (testArray2 == [[testArray2 copy] autorelease]) ? @"YES" : @"NO");
        NSLog(@"Is %@ the same as its copy: %@ ?", [testArray3 class],
    (testArray3 == [[testArray3 copy] autorelease]) ? @"YES" : @"NO");

        CFRelease(testArray1);
        [pool release];
        return 0;
    }

    Essentially, as an implementation detail, NSCFArray stores
    information about whether it is immutable or not.  If it thinks it's
    immutable then it raises an exception if you try to mutate it, but it
    doesn't bother to respond NO to -respondsToSelector:@selector
    (addObject:).
  • Wasn't the original motivation for immutable objects to simplify the
    implementation of multi-threaded or distributed code?

    _murat

    On Sep 26, 2007, at 5:19 PM, Erik Buck wrote:

    > There are in theory substantial advantages to using immutable
    > objects and immutable collections.  One theoretical advantage of
    > immutable objects is substantial optimization from implementing -
    > copy as
    >
    > - (id)copy
    > {
    > return [self retain];
    > }
    >
    > In theory, collection enumerators get simpler (and faster) when
    > used with immutable collections.  Pass by value (which can be
    > expensive) is less necessary when using immutable objects.  It is
    > safe to directly return immutable instance variables...
    >
    > I don't know if NeXT or Apple ever provided truly immutable objects
    > like NSString, NSArray, etc.  With the adoption of Core Foundation
    > and toll free bridged classes, any theoretical advantages of true
    > immutability were completely abandoned.  Without true immutability,
    > it is certainly valid to wonder why Cocoa maintains the (now
    > artificial) distinction between supposedly immutable classes and
    > their mutable subclasses.
    >
    > Sadly, I suspect the only reason that NSMutableArray and friends
    > continue to exist is so that "legacy" Cocoa code would not break.
    > Nothing stops Apple from re-factoring and moving the mutation
    > methods into the base classes now.  NSMutableArray and friends
    > would just become vestigial framework support for "old" code.
    >
    > Alternatively, Apple could decide to create NSCFImmutableArray or
    > take other steps to restore true immutability.  Apple is
    > inscrutable!  From my perspective, Apple currently has the worst of
    > both worlds: They have the clutter and complexity of distinctions
    > between mutable and immutable classes; They don't get any benefits
    > from immutable classes because they aren't really immutable.
  • The following simple example illustrates the reality of writing an
    algorithm that relies on a supposedly immutable object actually being
    immutable.

    Here's the output so you don't have to built it yourself to find out
    the stunning conclusion:

    2007-09-26 22:21:35.570 TestMutability2[1510] This algorithm depends
    on an object that claims to be an NSArray really being immutable
    2007-09-26 22:21:35.571 TestMutability2[1510] Supposedly immutable
    widgetsArray has class: NSCFArray
    2007-09-26 22:21:35.572 TestMutability2[1510] widgetsArray contents:
    (A, B, C)
    2007-09-26 22:21:35.572 TestMutability2[1510] widgetsArray contents:
    (A, B, C)
    2007-09-26 22:21:35.572 TestMutability2[1510] widgetsArray contents:
    (A, C)
    2007-09-26 22:21:35.572 TestMutability2[1510] Opps! The immutable
    widgetsArray changed behind my back.
    2007-09-26 22:21:35.573 TestMutability2[1510] This algorithm
    addresses the reality that something claiming to be immutable might
    not really be immutable.  You must always make a copy!
    2007-09-26 22:21:35.588 TestMutability2[1510] My copy of widgetsArray
    has class: NSCFArray
    2007-09-26 22:21:35.588 TestMutability2[1510] copy of widgetsArray
    contents: (A, B, C)
    2007-09-26 22:21:35.588 TestMutability2[1510] copy of widgetsArray
    contents: (A, B, C)
    2007-09-26 22:21:35.589 TestMutability2[1510] copy of widgetsArray
    contents: (A, B, C)

    Here's the program:
    #import <Foundation/Foundation.h>

    @interface MYWidgetManager : NSObject
    {
        NSMutableArray    *widgetArray;
    }

    - (id)init;
    - (void)dealloc;

    - (NSArray *)widgets;
    - (void)removeWidget:(id)aWidget;

    @end

    int main (int argc, const char * argv[])
    {
        // Oh dear! My algorithm tries to avoid a copy because
    widgetsArray is supposed to be immutable
        {
          NSAutoreleasePool    *pool = [[NSAutoreleasePool alloc] init];

          NSLog(@"This algorithm depends on an object that claims to be
    an NSArray really being immutable");

          MYWidgetManager      *testManager = [[[MYWidgetManager alloc]
    init] autorelease];
          NSArray              *widgetsArray = [testManager widgets];
          int                  i;
          const int            countOfWidgetsArray = [widgetsArray count];

          NSLog(@"Supposedly immutable widgetsArray has class: %@",
    [widgetsArray class]);
          NS_DURING
          for(i = 0; i < countOfWidgetsArray; i++)
          {
              NSLog(@"widgetsArray contents: %@", widgetsArray);
              if(NSOrderedSame == [@"B" compare:[widgetsArray
    objectAtIndex:i]])
              {
                [testManager removeWidget:[widgetsArray objectAtIndex:i]];
              }
          }
          NS_HANDLER
              NSLog(@"Opps! The immutable widgetsArray changed behind my
    back.");
          NS_ENDHANDLER

          [pool release];
        }

        // What happens if I make a copy ?
        {
          NSAutoreleasePool    *pool = [[NSAutoreleasePool alloc] init];

          NSLog(@"This algorithm addresses the reality that something
    claiming to be immutable might not really be immutable.  You must
    always make a copy!");

          MYWidgetManager      *testManager = [[[MYWidgetManager alloc]
    init] autorelease];
          NSArray              *widgetsArray = [[[testManager widgets]
    copy] autorelease];
          int                  i;
          const int            countOfWidgetsArray = [widgetsArray count];

          NSLog(@"My copy of widgetsArray has class: %@", [widgetsArray
    class]);
          for(i = 0; i < countOfWidgetsArray; i++)
          {
              NSLog(@"copy of widgetsArray contents: %@", widgetsArray);
              if(NSOrderedSame == [@"B" compare:[widgetsArray
    objectAtIndex:i]])
              {
                [testManager removeWidget:[widgetsArray objectAtIndex:i]];
              }
          }

          [pool release];
        }

        return 0;
    }

    @implementation MYWidgetManager

    - (id)init
    {
        if(nil != (self = [super init]))
        {
          widgetArray = [[NSMutableArray alloc] initWithObjects:@"A",
    @"B", @"C", nil];
        }

        return self;
    }

    - (void)dealloc
    {
        [widgetArray release];
        [super dealloc];
    }

    - (NSArray *)widgets
    {
        return widgetArray;
    }

    - (void)removeWidget:(id)aWidget
    {
        [widgetArray removeObject:aWidget];
    }

    @end
  • On Sep 26, 2007, at 10:01 PM, Murat Konar wrote:

    > Wasn't the original motivation for immutable objects to simplify
    > the implementation of multi-threaded or distributed code?
    >
    > _murat

    Maybe.  How does it do that?
  • On Sep 26, 2007, at 7:22 PM, Erik Buck wrote:

    > The following simple example illustrates the reality of writing an
    > algorithm that relies on a supposedly immutable object actually
    > being immutable.

    Contrived example. :)
  • > From: Greg Titus [mailto:<greg...>]
    > This is just my opinion, but I think that YES, absolutely,
    > subclassesshould know and depend upon the implementation details of their

    > superclasses.

    At this point, we part ways on the theory but thats fine.

    > To assume that you don't have to know how your own
    > superclass works is taking object-oriented theory too far, and just

    Um, this strikes me as really hard to defend.  Encapsulation is all about
    being able to wrap up "how" a class does its thing inside that class, and
    just worry about "what" it does.

    > leads to overly defensive programming, which means more redundant

    In this particular case, we've ended up with what I'd call "overly defensive
    programming" in the SUPERCLASS because it has no idea whether someone will
    come along and subclass it, so it complicates up its calls to alloc.  Thats
    just pushing the problem from the guy who knew he wanted to subclass into
    the guy who didn't know.

    Thats no big deal, except that it becomes mandatory to do "best-practices"
    when defining your classes since you can never tell whether you'll be
    subclassed a year from now.  And the original poster wanted to know what was
    "best practice", returning id or myclass*.

    > code. Not to mention the belief that you can just ignore how your
    > superclass is implemented leads to all sorts of bad code and
    > complacency. If you know what your superclass is doing, you write

    I dont understand how relying on my superclass to honor its interface
    contracts results in complacency or bad code.  I'd be interested in more
    faulty examples here.

    > (By a framework boundary I mean where your superclass is
    > in a different framework than the subclass. A subclass of NSView in
    > your own application, for instance.)

    which is, of course, why Apple recommend against subclassing NSString, etc.

    > But certainly in your own "Animals" framework, you better know how
    > your Animal superclass works, and you ought to take advantage
    > of that fact to write as little code as possible.

    You are saying that if Dog is part of the Animals framework, I can code it
    one way, but if I move it to another framework (which happens solely because
    of organisational differences on my development team), I should code it
    differently?

    > Apple advises using this pattern
    > rather than subclassing for class clusters:

    I don't see Animal as a class cluster here.

    > Finally, the design of Objective-C and Cocoa tends to lead to fairly
    > flat and broad inheritance hierarchies. There aren't that many deep
    > subclasses of subclasses of subclasses like you tend to find in some
    > other frameworks.

    I think I understand what you are saying here, and the conclusion is
    primarily that I still need to work on removing the C++isms from my thinking
    about Objective-C.

    I have another pet project which had developed a class hierarchy something
    like:

    basic_plugin
    file_format_plugin
      format_1_plugin
      format_2_plugin
    processing_plugin
      process_1_plugin
      process_2_plugin

    The 'basic_plugin' is a class that I use across all my applications, but it
    isn't in a framework, the source code is just copied into each application
    (along with the basic_plugin_manager that handles plugin folders etc at
    application startup).  file_format_plugin and processing_plugin are both
    specific to the current application, but neither of them want to concern
    themselves with the implementation of the basic_plugin logic, they just want
    to populate the standard entry methods and know that the level above them do
    the right thing.  Similiarly, some instances of format_1_plugin, etc are not
    built as part of the application at all, since they expect to be something
    developable by a 3rd party.  I can definitely see where adding in common
    superclasses under 'processing_plugin' for plugins that do similiar things
    is also quite likely.

    Do those lowest-level plugins really need to know about the internals all
    the way up the tree, or just up one level? Or can they rely on the theory
    that says "my superclass knows what he's doing and honors his interface
    contracts".  Which would imply that "if the method says it returns
    basic_plugin*, then thats all it returns".  Clumsy, not quite what you'd
    like, but 100% explicit and predictable.

    > Patterns like delegation keeps the number of
    > subclasses you need to write yourself to a minimum.

    ... but are irrelevant to the sorts of problems I'm thinking about.

    > of your own code ought to be a single step away from one of a few
    > "hub" superclasses: NSObject, NSView, NSWindowController. That means

    I'm somewhat surprised that most examples that people wheel out to discuss
    why Cocoa class hierarchies are shallow tend to be user-interface related.
    There are other domains of problems that *do* have deeply nested hierarchies
    for application specific reasons that a shallow hierarchy+nesting do not
    fit.

    > If he reimplements a superclass and doesn't check to see whether any
    > subclasses were depending upon the previous implementation,
    > I'd bitch at "the other guy" until he fixes it.

    No, he didn't reimplement the superclass.  He looked at a class that had a
    method of the form

    + fooFromBar:(Bar*)b
    {
    Foo *foo = [[[self class] alloc] initWithBar:b];
    }

    and because some other part of the system told him that we was spending a
    significant amount of time doing that particular function (lets not get
    distracted onto algorithm design, he's making a mistake, remember), he sees
    that that [self class] seems a bit wasteful.  So he changed it to

    + (Foo*)fooFromBar:(Bar*)b
    {
    Foo *foo = [[Foo alloc] initWithBar:b];
    }

    and then he tested his changes to death in his own test harnesses, none of
    which exercised "subclassing Foo".  How does he tell that someone else
    depended on that fairly subtle behaviour?
  • I take back my hasty and erroneous claim that there is no advantage
    to using immutable objects with the current version of Cocoa.  It was
    just bone headed and stupid and ill-informed and I know better.

    The "trick" is that when coding your own algorithms that depend on
    immutability, you absolutely must copy whatever immutable object you
    are using.  If the object was truly immutable, the copy is very cheap
    and just retains and returns the same object again.  If the object
    wasn't truly immutable, the copy may be expensive, but it's necessary
    and will produce the needed immutable object.

    Given these facts of life:
    If you are able to code an algorithm that works even if the relevant
    objects are mutable, you should probably do that.  Attempting to code
    an algorithm that requires immutability may end up forcing you to
    make copies of objects.  If performance is a concern, you must
    determine whether having to make copies is more or less efficient
    that just using an algorithm that doesn't rely on immutability.

    This all comes down to the Liskov object oriented substitution
    principle: http://www.eventhelix.com/RealtimeMantra/Object_Oriented/
    liskov_substitution_principle.htm

    You must be able to use an NSMutableArray in any situation where an
    NSArray can be used because NSMutableArray is a subclass on NSArray.
    However, because of this, no algorithm can safely assume that the
    NSArray it was just given is really immutable.
  • Erik stepped back on this, but I had this typed up so here goes:

    > On Sep 26, 2007, at 7:22 PM, Erik Buck wrote:
    >
    >> The following simple example illustrates the reality of writing an
    >> algorithm that relies on a supposedly immutable object actually
    >> being immutable.
    >
    > Contrived example. :)

    Not so much contrived as dependent on the mistaken belief that

          NSArray *widgetsArray = [testManager widgets];

    returns a reference to the existing one, rather than a *new* array.  The
    documentation of [testManager widgets] sould presumably make clear that you
    have the original array, not a copy and that calling removeWidget: will
    alter it (breaking the rule that you aren't allowed to change an NSArray
    while an NSEnumerator is running over it).

    Perhaps it ties into Greg Titus's argument that you should never subclass
    without knowing the full implementation detail of your superclass, but goes
    one step worse and implies you should never use *any* class unless you know
    its full implementation detail.

    (ie, there is no guarantee that the testManager is implementing the
    collection of widgets as an NSArray, it may be an NSDictionary which it
    walks over to satisty the [... widgets] request - if you knew this detail,
    you would never have assumed you had the original NSArray)
  • On Sep 26, 2007, at 10:51 PM, Jeff Laing wrote:

    > Erik stepped back on this, but I had this typed up so here goes:
    >
    >> On Sep 26, 2007, at 7:22 PM, Erik Buck wrote:
    >>
    >>> The following simple example illustrates the reality of writing an
    >>> algorithm that relies on a supposedly immutable object actually
    >>> being immutable.
    >>
    >> Contrived example. :)
    >
    > Not so much contrived as dependent on the mistaken belief that
    [Deleted]

    Yes.  It was slightly contrived in order to make it as concise as
    possible.  This issue really manifests in much more complicated
    situations when it would be nearly impossible to know that an
    innocent message will have catastrophic mutating effects on a
    seemingly unrelated object.

    Consider the slightly less contrived example: You are looping through
    a collection of widgets to determine if a particular object is
    present.  Aha! The object is there so you generate a notification
    intending for the notification to result in a user interface update.
    The observer of the notification responds by asking the widget
    manager to re-sort the array before updating the user interface
    because you wouldn't want to display an unsorted collection of
    widgets.  Um, Ooops!  That supposedly immutable collection you were
    looping through just mutated.

    The only way to reliably avoid this unfortunate situation is to copy
    the collection of widgets and loop through the copy.
  • On Sep 26, 2007, at 7:35 PM, Jeff Laing wrote:

    >> From: Greg Titus [mailto:<greg...>]
    >> This is just my opinion, but I think that YES, absolutely,
    >> subclasses should know and depend upon the implementation details
    >> of their
    >> superclasses.
    >
    > At this point, we part ways on the theory but thats fine.
    >
    >> To assume that you don't have to know how your own
    >> superclass works is taking object-oriented theory too far, and just
    >
    > Um, this strikes me as really hard to defend.  Encapsulation is all
    > about
    > being able to wrap up "how" a class does its thing inside that
    > class, and
    > just worry about "what" it does.

    Yes, but encapsulation is just for _using_ a class. If you are
    subclassing you have (at least by default) access to all of the
    superclass's instance variables and non-public methods. There's no
    encapsulation there. If you ever touch any of them, you need to know
    the superclass's implementation. If you are overriding existing
    methods to have new behavior, you need to know the superclass's
    implementation.

    If you don't ever touch the superclass's internal state, why aren't
    you using a Composite pattern instead? That's the only way to let its
    implementation change out from under you (as long as its public API
    remains the same) and still be sure that you won't be broken.

    > In this particular case, we've ended up with what I'd call "overly
    > defensive
    > programming" in the SUPERCLASS because it has no idea whether
    > someone will
    > come along and subclass it, so it complicates up its calls to
    > alloc.  Thats
    > just pushing the problem from the guy who knew he wanted to
    > subclass into
    > the guy who didn't know.

    Well, in the particular example given (+myClassWithFoo:) this is an
    extremely common pattern to alloc/init/autorelease a new instance. I
    don't think it complicates anything. If I was the superclass
    implementor, I would habitually use [self alloc], which would then
    work fine with subclasses. If I was the subclass implementor and
    wanted to provide support for creation of my subclass with
    +myClassWithFoo:, I would need to go look at the superclass
    implementation, because I need to know that -initWithFoo: is the
    initializer (I would probably guess that was the case, but I'd verify
    that). If the superclass author _hadn't_ used [self alloc] and had
    hardcoded the class name instead, I would change the superclass code
    to use [self alloc] while I was there.

    This brings up the point in another way that I'd previously alluded
    to: I tend to see subclass/superclass relationships as parts of the
    same "chunk" of code and modify inheritance subtrees all together. I
    don't expect to be able to implement a subclass without also possibly
    modifying the superclass or vice versa. You should always be willing
    to refactor as you go along.

    The exception is with a very few "hub" classes that are usually meant
    to be subclassed across framework boundaries, like NSView. And there
    you are explicitly writing your superclass with the expectation that
    it'll be subclassed by perhaps many people in the future. And you
    write defensively, and you document. But those examples are quite
    rare. Most inheritance relationships (especially deeper trees,
    especially model code) are all within the same domain and chunk of
    code, and it makes much more sense to just be aware of the
    implementation of that tree as a whole rather than to try to
    artificially limit code changes to single classes.

    > Thats no big deal, except that it becomes mandatory to do "best-
    > practices"
    > when defining your classes since you can never tell whether you'll be
    > subclassed a year from now.  And the original poster wanted to know
    > what was
    > "best practice", returning id or myclass*.

    I'd always return MyClass * for exactly the reasons you already gave
    when you originally answered this question. And no, it isn't
    mandatory to do "best-practices" if you don't think you'll be
    subclassed. That's exactly the same sort of over defensiveness in the
    other direction. When someone wants to come along and subclass you,
    they _make_ you cleanly subclassable _then_. In my world that
    programmer needs to understand your implementation before they try to
    subclass you, so they ought to be in your code checking it out.
    (Again, the exception being those few classes you 'export' as planned
    for subclassability.)

    >> code. Not to mention the belief that you can just ignore how your
    >> superclass is implemented leads to all sorts of bad code and
    >> complacency. If you know what your superclass is doing, you write
    >
    > I dont understand how relying on my superclass to honor its interface
    > contracts results in complacency or bad code.  I'd be interested in
    > more
    > faulty examples here.

    My argument is that if all you need is for it to honor its interface
    contract then you generally don't need to subclass. You just need to
    instantiate an instance of the superclass and message it using that
    interface contract.

    >> (By a framework boundary I mean where your superclass is
    >> in a different framework than the subclass. A subclass of NSView in
    >> your own application, for instance.)
    >
    > which is, of course, why Apple recommend against subclassing
    > NSString, etc.

    Right. I would never see the need to subclass NSString. It isn't
    really meant for that.

    >> But certainly in your own "Animals" framework, you better know how
    >> your Animal superclass works, and you ought to take advantage
    >> of that fact to write as little code as possible.
    >
    > You are saying that if Dog is part of the Animals framework, I can
    > code it
    > one way, but if I move it to another framework (which happens
    > solely because
    > of organisational differences on my development team), I should
    > code it
    > differently?

    Yes, absolutely. Organizational differences matter. Either there is
    an organizational wall between the two classes or there isn't. And if
    there is no programmer organizational wall between a super/subclass
    then I don't think you should bother constructing an artificial wall
    in your mind. If both are within your domain, you should look at both
    when you modify either. Continually refactor what code is in which of
    the classes as you work.

    >> Apple advises using this pattern
    >> rather than subclassing for class clusters:
    >
    > I don't see Animal as a class cluster here.

    No, me either. I was just using that as a pointer to an explanation
    of the Composite pattern.

    >> Finally, the design of Objective-C and Cocoa tends to lead to fairly
    >> flat and broad inheritance hierarchies. There aren't that many deep
    >> subclasses of subclasses of subclasses like you tend to find in some
    >> other frameworks.
    >
    > I think I understand what you are saying here, and the conclusion is
    > primarily that I still need to work on removing the C++isms from my
    > thinking
    > about Objective-C.

    Possibly. One of the reasons C++ hierarchies tend to be deeper is
    because C++ lacks things like categories. If I need some particular
    functionality on NSString I don't have to subclass or drop out of OO-
    land and use functions, I can add a category to add those methods to
    NSString. I can call all of the existing NSString public API from
    them, and don't need to be aware of the internal implementation.
    That's another way where if all you need is to access the existing
    public contract and don't need access to internals, you don't need to
    subclass in Objective-C.

    > I have another pet project which had developed a class hierarchy
    > something
    > like:
    >
    > basic_plugin
    > file_format_plugin
    > format_1_plugin
    > format_2_plugin
    > processing_plugin
    > process_1_plugin
    > process_2_plugin
    >
    > The 'basic_plugin' is a class that I use across all my
    > applications, but it
    > isn't in a framework, the source code is just copied into each
    > application
    > (along with the basic_plugin_manager that handles plugin folders
    > etc at
    > application startup).  file_format_plugin and processing_plugin are
    > both
    > specific to the current application, but neither of them want to
    > concern
    > themselves with the implementation of the basic_plugin logic, they
    > just want
    > to populate the standard entry methods and know that the level
    > above them do
    > the right thing.  Similiarly, some instances of format_1_plugin,
    > etc are not
    > built as part of the application at all, since they expect to be
    > something
    > developable by a 3rd party.  I can definitely see where adding in
    > common
    > superclasses under 'processing_plugin' for plugins that do similiar
    > things
    > is also quite likely.
    >
    > Do those lowest-level plugins really need to know about the
    > internals all
    > the way up the tree, or just up one level? Or can they rely on the
    > theory
    > that says "my superclass knows what he's doing and honors his
    > interface
    > contracts".  Which would imply that "if the method says it returns
    > basic_plugin*, then thats all it returns".  Clumsy, not quite what
    > you'd
    > like, but 100% explicit and predictable.

    Okay, the lowest level plugins only need to know about
    file_format_plugin and processing_plugin. You are exposing those
    classes to 3rd parties and expect them to be subclassed, therefore
    you know to write them defensively and document them, there's an
    organizational wall there.  As part of that defensiveness I would
    expect your file_format_plugin and processing_plugin to make clear
    what it returns via documentation in each method.

    I would argue that when you work on this chunk of code that you
    should consider basic_plugin, file_format_plugin, and
    processing_plugin all together, and it should be fine for the two
    subclasses to know of and take advantage of the basic_plugin internal
    implementation, and that you should always be refactoring the three
    together (moving common code up, adding new virtual methods on the
    superclass to call when the subclasses need to do something
    differently from each other in some shared situation, et cetera).

    In short, lavish your attention on careful interface contracts at
    organizational boundaries, but not at all on class boundaries within
    the same organization.

    >> Patterns like delegation keeps the number of
    >> subclasses you need to write yourself to a minimum.
    >
    > ... but are irrelevant to the sorts of problems I'm thinking about.

    Sure, ok. But another way that you could have designed your plugin
    architecture would be to have a file_format_plugin_protocol and a
    processing_plugin_protocol, and the actual plugins wouldn't have any
    mandated superclass at all, they'd just need to respond to particular
    methods. If there was shared behavior or ways of handling the plugins
    in your application, you'd have a file_format_plugin_helper and
    processing_plugin_helper which would have the shared code for
    managing those types of plugins and would just have a pointer to a
    plugin instance. That would be a composite pattern where the
    combination of your helper class and the plugin with unknown
    superclass would together act in the same role as your current plugin
    class. The helper does the management and flow of control and such
    and delegates the specific file format work (or whatever) to the plugin.

    That kind of structure has its advantages in not forcing the
    inheritance hierarchy on your 3rd parties. What if there was a 3rd
    party that had a file_format_plugin and a processing_plugin that
    shared a bunch of the same code? They would have liked to have used a
    common superclass for the two, but can't with your inheritance-based
    design. (At least not in Obj-C, which is only single inheritance. But
    don't get my ranting on the evils of multiple inheritance.) :-)

    >> of your own code ought to be a single step away from one of a few
    >> "hub" superclasses: NSObject, NSView, NSWindowController. That means
    >
    > I'm somewhat surprised that most examples that people wheel out to
    > discuss
    > why Cocoa class hierarchies are shallow tend to be user-interface
    > related.
    > There are other domains of problems that *do* have deeply nested
    > hierarchies
    > for application specific reasons that a shallow hierarchy+nesting
    > do not
    > fit.

    The examples are wheeled out in the user-interface domain because
    that's the only somewhat complicated domain that the Cocoa frameworks
    themselves cover. :-)  To talk about other domains requires a whole
    lot more explanation because we can't assume that we have classes/
    code/domain-knowledge in common.

    But yes, object models, for example, tend to have more deeply nested
    hierarchies. I think in general you're better off trying to use
    composition and delegation and similar tricks that Objective-C's
    dynamicism is well-suited for in order to end up with less depth than
    you might find in a C++ hierarchy in the same problem domain, but
    still, they'll generally be deeper.

    >> If he reimplements a superclass and doesn't check to see whether any
    >> subclasses were depending upon the previous implementation,
    >> I'd bitch at "the other guy" until he fixes it.
    >
    > No, he didn't reimplement the superclass.  He looked at a class
    > that had a
    > method of the form
    >
    > + fooFromBar:(Bar*)b
    > {
    > Foo *foo = [[[self class] alloc] initWithBar:b];
    > }
    >
    > and because some other part of the system told him that we was
    > spending a
    > significant amount of time doing that particular function (lets not
    > get
    > distracted onto algorithm design, he's making a mistake, remember),
    > he sees
    > that that [self class] seems a bit wasteful.

    Well, it should just be [[self alloc] initWithBar:b], but yes, he's
    making a mistake, right...

    > So he changed it to
    >
    > + (Foo*)fooFromBar:(Bar*)b
    > {
    > Foo *foo = [[Foo alloc] initWithBar:b];
    > }
    >
    > and then he tested his changes to death in his own test harnesses,
    > none of
    > which exercised "subclassing Foo".  How does he tell that someone else
    > depended on that fairly subtle behaviour?

    If Foo is a class that is 'exposed' at an organizational boundary and
    is expected and documented to be subclassed, it's his fault for
    subclassing not being part of his test harness.

    If the subclass(es) is/are within the same framework, he should be
    examining them as he is doing profiling and making code changes and
    they should be tested at the same time as the superclass.

    If the subclass(es) is/are external to his organization, and Foo
    isn't documented and intended to be subclassed, at that point it
    isn't his fault. It is the programmer who wrote the subclass's fault.
    That is the time when the subclass-er needs to be very careful about
    the contracted API, and avoid subclassing entirely, if possible. This
    is the situation in which I agree with your original comment on this
    topic, that the subclass-er shouldn't have counted on +fooFromBar:
    returning the subclass, and should have written their own
    +subclassFromBar: method instead, even if it ends up doing exactly
    the same thing.

    The last of those 3 situations is hopefully fairly rare. It means
    that there's either a mistake in the design of the class hierarchy
    (that something needed subclassing which wasn't expected to be), or
    there's a mistake in the management of the development team (that
    these two classes were split into two different organizations).

    Hope this explains more where I'm coming from,
    -- Greg
  • On Sep 26, 2007, at 7:23 PM, Erik Buck wrote:

    > On Sep 26, 2007, at 10:01 PM, Murat Konar wrote:
    >
    >> Wasn't the original motivation for immutable objects to simplify
    >> the implementation of multi-threaded or distributed code?
    >
    > Maybe.  How does it do that?

    By obviating the need for synchronization mechanisms (e.g. mutexes)
    when sharing immutable objects in separate execution threads. You
    can't have a race condition for objects that don't change.

    _murat
  • > The "trick" is that when coding your own algorithms that depend on
    > immutability, you absolutely must copy whatever immutable object
    > you are using.  If the object was truly immutable, the copy is very
    > cheap and just retains and returns the same object again.  If the
    > object wasn't truly immutable, the copy may be expensive, but it's
    > necessary and will produce the needed immutable object.

    > You must be able to use an NSMutableArray in any situation where an
    > NSArray can be used because NSMutableArray is a subclass on
    > NSArray.  However, because of this, no algorithm can safely assume
    > that the NSArray it was just given is really immutable.

    When you retrieve an array and then use it immediately, in the same
    method, you can often be pretty sure no other code is running that
    modifies that array*, so you know that regardless of the
    implementation it is not going to change (if you don't mutate it
    yourself, of course).  If you hang onto it past the end of your
    method, then you should be at least retaining it, or if you know you
    want it to remain unchanged, copying it.  And if you do something
    like call a getter for property X, then the setter for the same
    property, you should as someone with even a little ObjC experience
    guess that it probably will release the old value, so you should
    retain it beforehand if you want to ensure it stays valid.

    Now, it's true that because many objects just return their mutable
    ivars directly, you can hit problems with this.  For example, I've
    seen it crop up occasionally that people will ask for, say, a window
    title, and then do something which modifies it, and then, oh my, they
    just messaged a freed object or it changed or whatever.  This is why
    some people believe the correct approach for a getter is to retain/
    autorelease or copy/autorelease as appropriate.  I've seen arguments
    on this rage like you wouldn't believe, and I don't think there's an
    ideal solution... my opinion is that it's not worth the performance
    hit for a rare and [typically] easily diagnoses and resolved
    problem.  You're free to do things your own way, but like it or not
    there's plenty of people who share my approach, so you'll have to
    deal with it anyway.

    In terms of the perversion of returned types by the caller -
    'knowing' they're really mutable and mutating them - well.... that
    objects may return mutable instances where their API says they're
    immutable, that's typically a performance optimisation (often
    premature) and an internal detail, but if you're using strong types
    you'll probably be okay, because e.g. you'll be told that NSArray
    doesn't respond to addObject:.  Easy.  And if you go and cast it to a
    mutable array, well, you've broken your contract with that API,
    you've assumed an implementation detail, you've gone against the
    spirit of the API, so, it's hard to feel sorry for you.  It's your
    foot to shoot if you're so inclined.

    (granted it's possible you'll pass that array along N times, and then
    some object will innocently ask if it's mutable or not, and then go
    poke it... yes, it's true, it's a flaw in this system, but it's
    uncommon enough that you can just deal with it if it crops up...
    ignorance is bliss :D )

    Wade

    * = It doesn't matter if your app is multithreaded; if it is and
    you're sharing data between threads, you should be using
    synchronisation mechanisms at appropriate places to ensure this is
    true anyway.  Multithreading is a separate issue.
  • Am 27.09.2007 um 01:37 schrieb Jeff Laing:
    > Now, in this case, you can call
    >
    > Dog *pet = [Dog animalWithName:@"Rover"];
    >
    > but that feel uncomfortable to me.  I'd still be inclined to add
    > another
    > method so I could write:
    >
    > Dog *pet = [Dog dogWithName:@"Rover"];
    >
    > even if all it did was wrap up the same methods.

      One advantage of going with the "uncomfortable" variant is that you
    can have generic creation code. E.g. if you have plug-ins that
    contain new animals, you can do:

    Animal* foodSource = [[myBundle principalClass] animalWithName:
    [nameField stringValue]];

    Yes, this code could make you eat a dog, but then that's the whole
    point of polymorphism: As long as it is an animal, you treat it the
    same. Even better, you can load each plugin, add its principalClass
    to an array or dictionary, and then use the index or key to create a
    new object of these types based on user data, e.g. a popup selection.

      Another advantage is that you don't needlessly pollute the
    namespace with additional methods or introduce additional code paths.
    If you work with another programmer, that programmer may not be aware
    that animalWithName: exists if you provide a dogWithName:, and may
    not bother to search upwards in the class hierarchy. He'll override
    dogWithName to do some additional work (even if it's just debug
    output), and it'll work for all but those 10% of your dogs that
    someone creates through class pointers and animalWithName:.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • Am 27.09.2007 um 02:19 schrieb Erik Buck:
    > Alternatively, Apple could decide to create NSCFImmutableArray or
    > take other steps to restore true immutability.  Apple is
    > inscrutable!  From my perspective, Apple currently has the worst of
    > both worlds: They have the clutter and complexity of distinctions
    > between mutable and immutable classes; They don't get any benefits
    > from immutable classes because they aren't really immutable.

      Can you elaborate? I wasn't aware there was any problem with
    mutability/immutability and CoreFoundation.

      AFAIK the internals of CFString & Co. still know whether they are
    mutable or not and implement e.g. the "copy" optimization. NSCFString
    may not know, but since it effectively calls through to the CFString
    class's vtable (at least that's what I'd expect it to do), mutability
    and immutability would still work.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • Am 27.09.2007 um 04:35 schrieb Jeff Laing:
    > Which would imply that "if the method says it returns
    > basic_plugin*, then thats all it returns".
    > Clumsy, not quite what you'd like, but 100% explicit and predictable.

      Both C++ and Objective C never enforce this contract. An
    NSMutableArray is a subclass of an NSArray. So, every place that
    declares it returns an NSArray may return any of its subclasses.
    Already, you can't be sure that "that's all it returns".

      As I learned in the course of this thread, choosing "id" simply
    shuts up some warnings, and since it's a Cocoa convention that
    factory methods return an object of their class, or a subclass
    thereof, you can be reasonably sure that that's what you get even
    though it only says "id". Since Objective C doesn't support
    overriding methods by type, it's the best we can do in this language.
    Specifying a more specific type could cause the type mismatch
    warnings to become unusable, because you'd get hundreds of them.

      It ain't pretty, but unless ObjC gains type-based overriding like C+
    +, I don't see another solution offhand.

    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