Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.objective-c > #215 > unrolled thread

A question on designated initializers

Started byJon Rossen <jonr17@comcast.net>
First post2015-11-22 13:39 -0800
Last post2015-11-24 18:29 -0800
Articles 20 on this page of 32 — 5 participants

Back to article view | Back to comp.lang.objective-c


Contents

  A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 13:39 -0800
    Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 14:48 -0800
      Re: A question on designated initializers Louis Wu <louiswu@ringworld.net> - 2015-11-22 16:26 -0800
        Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 17:24 -0800
          Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-23 02:39 +0100
    Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-23 01:18 +0100
      Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 16:49 -0800
        Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-23 02:02 +0100
          Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-27 17:19 -0800
            Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-28 13:22 +0100
              Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-28 17:37 -0800
                Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-29 03:45 +0100
    Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-22 19:15 -0800
      Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-23 18:44 -0800
        Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-24 04:58 +0100
          Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-24 16:42 -0800
            Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 02:00 +0100
          Re: A question on designated initializers Greg Parker <gparker@apple.com> - 2015-11-25 01:25 -0800
        Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-24 07:38 -0800
          Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-24 15:54 -0800
            Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 01:49 +0100
              Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-24 18:51 -0800
                Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 04:41 +0100
                  Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-25 00:25 -0800
                    Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 14:16 +0100
                      Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-25 17:30 -0800
                        Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-26 03:03 +0100
                  Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-25 09:08 -0800
                    Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 18:41 +0100
                      Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-12-02 14:37 -0800
                        Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-12-03 00:58 +0100
            Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-24 18:29 -0800

Page 1 of 2  [1] 2  Next page →


#215 — A question on designated initializers

FromJon Rossen <jonr17@comcast.net>
Date2015-11-22 13:39 -0800
SubjectA question on designated initializers
Message-ID<n2tcmc$t64$1@news.albasani.net>
I have a rather beginner's basic type of question on designated 
initializers.  The book that I'm using uses an extremely simple (and 
trivial) example (which is usually the case) which leaves me with about 
as many questions as answers.

Here's the simple example from the book using a Rectangle class which is 
a child class of NSObject.

Here's some of the pertinent info from Rectangle.h; I won't bother 
including stuff that's not directly related to the example:

Rectangle.h

@property int width, height;

-(void) setWidth: (int) w andHeight: (int) h;
-(instancetype) initWithWidth: (int) w andHeight: (int) h;
-(instancetype) init;


Ok, here are just the new, custom init methods from Rectangle.m

Rectangle.m

//Designated initializer
-(instancetype) initWithWidth: (int) w andHeight: (int) h;
{
     self = [super init];
     if (self)
         [self setWidth:w andHeight:h];

     return self;
}

//The override of super's designated initializer
-(instancetype) init
{
     return [self initWithWidth:0 andHeight:0];
}


Ok, I get what's going on here:  You create a new designated initializer 
which handles a lot of the ivars that you feel are important.  You have 
to include a call super's designated intializer and assign it to self, 
then the 'if' test, then your initialization code.  You then override 
super's designated initializer (in this case it's NSObject's init) and 
return a call to your designated initializer.  I get how the hierarchy 
is supposed to work.

But how is this helpful?  In my mind it's not helpful for a few 
different reasons.  Reason 1: In the override to init you've hardwired 
the width and height to be 0.  That's what NSObject's init would do, so 
it's not doing anything new or better. Reason 2:  Even if you put 
different values in there say, 1 and 2 or whatever values you thought 
you might need a newly created Rectangle object to have, they are still 
hardwired values, buried inside the newly created init method.  How is 
this helpful at all?  You may want to have a bunch of rectangles of all 
different dimensions.  At that point you'd have to change their 
dimensions with the proper accessor method.  If so, why even both with 
this newly created init when you can just use NSObject's init and use 
the accessor method to tweak the height and width you want each 
Rectangle object to be?

Am I correct that the trivial nature of this example sort of gets in the 
way of truly understanding the benefit or need of all of this?
thanks,
jonR





[toc] | [next] | [standalone]


#216

FromJon Rossen <jonr17@comcast.net>
Date2015-11-22 14:48 -0800
Message-ID<n2tgoe$59u$1@news.albasani.net>
In reply to#215
On 11/22/2015 1:39 PM, Jon Rossen wrote:
> I have a rather beginner's basic type of question on designated
> initializers.  The book that I'm using uses an extremely simple (and
> trivial) example (which is usually the case) which leaves me with about
> as many questions as answers.
>
> Here's the simple example from the book using a Rectangle class which is
> a child class of NSObject.
>
> Here's some of the pertinent info from Rectangle.h; I won't bother
> including stuff that's not directly related to the example:
>
> Rectangle.h
>
> @property int width, height;
>
> -(void) setWidth: (int) w andHeight: (int) h;
> -(instancetype) initWithWidth: (int) w andHeight: (int) h;
> -(instancetype) init;
>
>
> Ok, here are just the new, custom init methods from Rectangle.m
>
> Rectangle.m
>
> //Designated initializer
> -(instancetype) initWithWidth: (int) w andHeight: (int) h;
> {
>      self = [super init];
>      if (self)
>          [self setWidth:w andHeight:h];
>
>      return self;
> }
>
> //The override of super's designated initializer
> -(instancetype) init
> {
>      return [self initWithWidth:0 andHeight:0];
> }
>
>
> Ok, I get what's going on here:  You create a new designated initializer
> which handles a lot of the ivars that you feel are important.  You have
> to include a call super's designated intializer and assign it to self,
> then the 'if' test, then your initialization code.  You then override
> super's designated initializer (in this case it's NSObject's init) and
> return a call to your designated initializer.  I get how the hierarchy
> is supposed to work.
>
> But how is this helpful?  In my mind it's not helpful for a few
> different reasons.  Reason 1: In the override to init you've hardwired
> the width and height to be 0.  That's what NSObject's init would do, so
> it's not doing anything new or better. Reason 2:  Even if you put
> different values in there say, 1 and 2 or whatever values you thought
> you might need a newly created Rectangle object to have, they are still
> hardwired values, buried inside the newly created init method.  How is
> this helpful at all?  You may want to have a bunch of rectangles of all
> different dimensions.  At that point you'd have to change their
> dimensions with the proper accessor method.  If so, why even both with
> this newly created init when you can just use NSObject's init and use
> the accessor method to tweak the height and width you want each
> Rectangle object to be?
>
> Am I correct that the trivial nature of this example sort of gets in the
> way of truly understanding the benefit or need of all of this?
> thanks,
> jonR
>

DOH!!!  It's amazing how asking a question sometimes helps you 
understand an issue even before you get a response from someone.  After 
posting the question, I looked at the code again and came to a 
conclusion that will either refine the question or answer it.  From what 
I can see, my question about the use value of the newly written init for 
the Fraction class is off-base because it really doesn't offer anything 
for the Fraction class, and is not *meant* to offer anything directly. 
It's use value is for any subclasses that Fraction may have.  For 
example, if Fraction's subclass needs to create it's own designated 
initializer, it will contain a call to super's init (self = [super 
init], and for Fraction's subclass, init will be the Fraction class 
version of init, which returns a call to Fraction's designated 
initializer.  So, in this sense, the override of init aids in the 'chain 
reaction' so to speak of how all these things work together in getting 
all the ivars initialized properly.  Am i on the right track here?
thx, jonR



[toc] | [prev] | [next] | [standalone]


#218

FromLouis Wu <louiswu@ringworld.net>
Date2015-11-22 16:26 -0800
Message-ID<louiswu-F7B886.16264522112015@news.giganews.com>
In reply to#216
In article <n2tgoe$59u$1@news.albasani.net>,
 Jon Rossen <jonr17@comcast.net> wrote:

[snipt]
> >
> > Ok, here are just the new, custom init methods from Rectangle.m
> >
> > Rectangle.m
> >
> > //Designated initializer
> > -(instancetype) initWithWidth: (int) w andHeight: (int) h;
> > {
> >      self = [super init];
> >      if (self)
> >          [self setWidth:w andHeight:h];
> >
> >      return self;
> > }
> >
> > //The override of super's designated initializer
> > -(instancetype) init
> > {
> >      return [self initWithWidth:0 andHeight:0];
> > }
> >
> >
> > Ok, I get what's going on here:  You create a new designated initializer
> > which handles a lot of the ivars that you feel are important.  You have
> > to include a call super's designated intializer and assign it to self,
> > then the 'if' test, then your initialization code.  You then override
> > super's designated initializer (in this case it's NSObject's init) and
> > return a call to your designated initializer.  I get how the hierarchy
> > is supposed to work.


You SHOULD use the designated initializer for creating objects of the 
class because it was set up to ensure that the object is initialized in 
a safe and consistent manner.

However, since your new class may be initialized by some other means, 
the basic form of the init should be overridden to make sure that the 
designated initializer is used to create the object.

Suppose, for instance that you need to create Rectangle instances by 
using only the class name using ...

NSObject *myRect = [[NSClassFromString(@"Rectangle") alloc] init];

Ensuring that the init method (which you overrode) calls the designated 
initializer will make sure that this behaviour "just works".

[toc] | [prev] | [next] | [standalone]


#221

FromJon Rossen <jonr17@comcast.net>
Date2015-11-22 17:24 -0800
Message-ID<n2tpt4$3pn$1@news.albasani.net>
In reply to#218
> You SHOULD use the designated initializer for creating objects of the
> class because it was set up to ensure that the object is initialized in
> a safe and consistent manner.
>
> However, since your new class may be initialized by some other means,
> the basic form of the init should be overridden to make sure that the
> designated initializer is used to create the object.



> Suppose, for instance that you need to create Rectangle instances by
> using only the class name using ...

> NSObject *myRect = [[NSClassFromString(@"Rectangle") alloc] init];
>
> Ensuring that the init method (which you overrode) calls the designated
> initializer will make sure that this behaviour "just works".

Thanks for the response!

I do not understand this example using NSClassFromString that you have 
provided.  In my original post, when I said this was a beginner's 
question, I sort of meant it. :-)  When using your example:
NSObject *myRect = [[NSClassFromString(@"Rectangle") alloc] init]; an 
instance of the Rectangle class is not created.  Was there a typo? Did 
you mean to state: Rectangle *myRect....??

Also, why is the importance of having overridden init specifically 
illustrated by this example using NSClassFromString?  Again, I'm 
assuming that my lack of understanding of the simple example you have 
chosen is the reason why I am not understanding your point.

-jonR

[toc] | [prev] | [next] | [standalone]


#222

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-23 02:39 +0100
Message-ID<87a8q5wl3f.fsf@kuiper.lan.informatimago.com>
In reply to#221
Jon Rossen <jonr17@comcast.net> writes:

>> You SHOULD use the designated initializer for creating objects of the
>> class because it was set up to ensure that the object is initialized in
>> a safe and consistent manner.
>>
>> However, since your new class may be initialized by some other means,
>> the basic form of the init should be overridden to make sure that the
>> designated initializer is used to create the object.
>
>
>
>> Suppose, for instance that you need to create Rectangle instances by
>> using only the class name using ...
>
>> NSObject *myRect = [[NSClassFromString(@"Rectangle") alloc] init];
>>
>> Ensuring that the init method (which you overrode) calls the designated
>> initializer will make sure that this behaviour "just works".
>
> Thanks for the response!
>
> I do not understand this example using NSClassFromString that you have
> provided.  In my original post, when I said this was a beginner's
> question, I sort of meant it. :-)  When using your example:
> NSObject *myRect = [[NSClassFromString(@"Rectangle") alloc] init]; an
> instance of the Rectangle class is not created.  

Yes it is.

   [NSClassFromString(@"Rectangle") alloc]

is the same as:

   [Rectangle alloc]

But we could have:

   NSString* className=[file readLine];
   NSObject* object=[[NSClassFromString(className) alloc] init];

and obtain an object of a class unknown at compilation time.


> Was there a typo? Did
> you mean to state: Rectangle *myRect....??

Rectangle being a subclass of NSObject, it's basically the same.

Objective-C is a dynamically typed programming language: there's a
run-time type associated with the value of the object, independent on
the C type associated to the variable.

For objects, you could as well always use the id type for all objects.

The only advantage of using a specific (super)class to define the type
of variables holding objects, is that it allows the compiler to issue
warning about the messages sent to the objects bound to that variable.



> Also, why is the importance of having overridden init specifically
> illustrated by this example using NSClassFromString?  

Again, the example is insufficient, since a literal class name was
given.  But the example shows that this class name can be determined at
run-time (it is little probable that the compiler would optimize out the
call to NSClassFromString).


> Again, I'm assuming that my lack of understanding of the simple
> example you have chosen is the reason why I am not understanding your
> point.

Definitely.

You should remember that Objective-C, the object parts, are very
dynamic: everything can be done at run-time.  You can create new classes
at run-time, you can define new methods at run-time, you can instanciate
those classes, and you can send messages at run-time, that weren't
defined at compilation time and that the compiler knows nothing about.

The only thing that is a little harder to do in C at run-time, is to
actually compile a new method body.  But using LLVM or dynamic loading
of libraries (that you may have compiled at run-time invoking an
external C compiler), you could also define new method bodies at
run-time.

Now, clearly, this is more difficult to do that in Objective-C than in
Smalltalk or Lisp (and Apple forbids it on iOS).  But this is still what
Objective-C is, and this is the big difference between Objective-C and
C++.



-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

[toc] | [prev] | [next] | [standalone]


#217

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-23 01:18 +0100
Message-ID<87r3jhwosu.fsf@kuiper.lan.informatimago.com>
In reply to#215
Jon Rossen <jonr17@comcast.net> writes:

> Am I correct that the trivial nature of this example sort of gets in
> the way of truly understanding the benefit or need of all of this?

An initializer method may do a little more than just setting ivars.

Basically, what it has to do, is to establish the class invariant for
the instance.  This class invariant is not necessarily set by
 -[NSObject init].


Now, when you specify for a class a new designated initializer, this
creates a new initializer for all its subclasses.  That is, a client of
the class or any of the subclasses, may and you can assume, will use any
of those initializers.

Therefore, if you have a new designated initializer, it might set up the
class invariant, while the other initializers in the super class might
not (probably won't).  This is the reason why you need to define
overridden initializers for the non-designated initializer too.



For example, you might have a serialization/deserialization library
(eg. -[NSBundle loadNibNamed:]); there's no mechanism designed to
specify the designated initializer (and it's arguments and valid values)
to recreate the deserialized objects.  Instead, the library will just
do: [[class alloc] init] calling the default initializer, and then it
will set the saved ivars directly.


Now, indeed, in this small example, we can assume that -[NSObject init]
ensures the Rectangle class invariant (which we may assume to be
(0<=width)&&(0<=height)), and calling -[Rectangle init] does nothing
more than -[NSObject init], and therefore as you write,  this example
sort of gets in the way of truly understanding the benefit or need of
all of this?

Let's say that a Rectangle shall not be a line or a point:

    -(bool)invariant{
        return((0<width)&&(0<height));
    }

Then:

    //Designated initializer
    -(id)initWithWidth: (int) w andHeight: (int) h;
    {
        self = [super init];
        if(self){ // always use braces!
                  // https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-goto-fail-apples-ssl-bug-explained-plus-an-unofficial-patch/
            [braces setWidth:max(1,w) andHeight:max(1,h)];
        }
        assert([self invariant]);
        return self;
    }


    //The override of super's designated initializer
    -(id)init{
        // smallest rectangle.
        return [self initWithWidth:1 andHeight:1];
    }


Then it will be obvious that -[NSObject init] doesn't 
ensure [self invariant], and that -[Rectangle init] is needed.


-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

[toc] | [prev] | [next] | [standalone]


#219

FromJon Rossen <jonr17@comcast.net>
Date2015-11-22 16:49 -0800
Message-ID<n2tnq0$gs5$1@news.albasani.net>
In reply to#217

On 11/22/2015 4:18 PM, Pascal J. Bourguignon wrote:
> Jon Rossen <jonr17@comcast.net> writes:
>
>> Am I correct that the trivial nature of this example sort of gets in
>> the way of truly understanding the benefit or need of all of this?
>
> An initializer method may do a little more than just setting ivars.
>
> Basically, what it has to do, is to establish the class invariant for
> the instance.  This class invariant is not necessarily set by
>   -[NSObject init].
>
>
> Now, when you specify for a class a new designated initializer, this
> creates a new initializer for all its subclasses.  That is, a client of
> the class or any of the subclasses, may and you can assume, will use any
> of those initializers.
>
> Therefore, if you have a new designated initializer, it might set up the
> class invariant, while the other initializers in the super class might
> not (probably won't).  This is the reason why you need to define
> overridden initializers for the non-designated initializer too.
>
>
>
> For example, you might have a serialization/deserialization library
> (eg. -[NSBundle loadNibNamed:]); there's no mechanism designed to
> specify the designated initializer (and it's arguments and valid values)
> to recreate the deserialized objects.  Instead, the library will just
> do: [[class alloc] init] calling the default initializer, and then it
> will set the saved ivars directly.
>
>
> Now, indeed, in this small example, we can assume that -[NSObject init]
> ensures the Rectangle class invariant (which we may assume to be
> (0<=width)&&(0<=height)), and calling -[Rectangle init] does nothing
> more than -[NSObject init], and therefore as you write,  this example
> sort of gets in the way of truly understanding the benefit or need of
> all of this?
>
> Let's say that a Rectangle shall not be a line or a point:
>
>      -(bool)invariant{
>          return((0<width)&&(0<height));
>      }
>
> Then:
>
>      //Designated initializer
>      -(id)initWithWidth: (int) w andHeight: (int) h;
>      {
>          self = [super init];
>          if(self){ // always use braces!
>                    // https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-goto-fail-apples-ssl-bug-explained-plus-an-unofficial-patch/
>              [braces setWidth:max(1,w) andHeight:max(1,h)];
>          }
>          assert([self invariant]);
>          return self;
>      }
>
>
>      //The override of super's designated initializer
>      -(id)init{
>          // smallest rectangle.
>          return [self initWithWidth:1 andHeight:1];
>      }
>
>
> Then it will be obvious that -[NSObject init] doesn't
> ensure [self invariant], and that -[Rectangle init] is needed.
>
>

Pascal,

Thanks for the explanations and to be honest I don't fully understand 
all of what you have said, but I understand it enough for it to make 
sense.  However, I realize I need to understand class invariants which 
is a new concept to me.  I think that is the primary component or 
background for me that is lacking and when I get a better understanding 
I'll fully understand your explanation.
Cheers,
jonR

[toc] | [prev] | [next] | [standalone]


#220

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-23 02:02 +0100
Message-ID<87egfhwmsj.fsf@kuiper.lan.informatimago.com>
In reply to#219
Jon Rossen <jonr17@comcast.net> writes:

> Thanks for the explanations and to be honest I don't fully understand
> all of what you have said, but I understand it enough for it to make
> sense.  However, I realize I need to understand class invariants which
> is a new concept to me.  I think that is the primary component or
> background for me that is lacking and when I get a better
> understanding I'll fully understand your explanation.

Have a look at:

https://en.wikipedia.org/wiki/Liskov_substitution_principle


-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass Autodesk CEO

[toc] | [prev] | [next] | [standalone]


#241

FromJon Rossen <jonr17@comcast.net>
Date2015-11-27 17:19 -0800
Message-ID<n3avf4$37g$1@news.albasani.net>
In reply to#220
On 11/22/2015 5:02 PM, Pascal J. Bourguignon wrote:
> Jon Rossen <jonr17@comcast.net> writes:
>
>> Thanks for the explanations and to be honest I don't fully understand
>> all of what you have said, but I understand it enough for it to make
>> sense.  However, I realize I need to understand class invariants which
>> is a new concept to me.  I think that is the primary component or
>> background for me that is lacking and when I get a better
>> understanding I'll fully understand your explanation.
>
> Have a look at:
>
> https://en.wikipedia.org/wiki/Liskov_substitution_principle
>
>

Geez, interesting but did you really think someone who calls himself a 
relative beginner that stated he needed help understanding class 
invariants could learn about them by reading this?  You almost need to 
know all about class invariants *first* in order to understand this 
Wikipedia article.  Around the time I would start to sort things out I'd 
get another obscure set of factoids and jargon thrown at me that would 
create a new set of uncertainty and eventually I spiraled down the 
rabbit hole of confusion scratching my head and saying WTF!  :-)  I'm 
sure with a few Google searches I may come up with some other stuff to 
read and perhaps with all of them it will start to become clear.  Thanks 
for at least taking the time to try and get me on the correct path.
-jonR

[toc] | [prev] | [next] | [standalone]


#242

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-28 13:22 +0100
Message-ID<87poyus47x.fsf@kuiper.lan.informatimago.com>
In reply to#241
Jon Rossen <jonr17@comcast.net> writes:

> On 11/22/2015 5:02 PM, Pascal J. Bourguignon wrote:
>> Jon Rossen <jonr17@comcast.net> writes:
>>
>>> Thanks for the explanations and to be honest I don't fully understand
>>> all of what you have said, but I understand it enough for it to make
>>> sense.  However, I realize I need to understand class invariants which
>>> is a new concept to me.  I think that is the primary component or
>>> background for me that is lacking and when I get a better
>>> understanding I'll fully understand your explanation.
>>
>> Have a look at:
>>
>> https://en.wikipedia.org/wiki/Liskov_substitution_principle
>>
>>
>
> Geez, interesting but did you really think someone who calls himself a
> relative beginner that stated he needed help understanding class
> invariants could learn about them by reading this?  

Of course not.  This is just a pointer to search for further references.
The newbie shall find a book or some on-line course about the subject.

> You almost need to know all about class invariants *first* in order to
> understand this Wikipedia article.  Around the time I would start to
> sort things out I'd get another obscure set of factoids and jargon
> thrown at me that would create a new set of uncertainty and eventually
> I spiraled down the rabbit hole of confusion scratching my head and
> saying WTF!  :-) I'm sure with a few Google searches I may come up
> with some other stuff to read and perhaps with all of them it will
> start to become clear.  Thanks for at least taking the time to try and
> get me on the correct path.

Also I would tend to assume a CS formation for newbie programmers.
Granted this is probably an error, and also it looks like a lot of
universities or programming schools fail to teach anything to students
worth the money they paiy for it, but I fail to see how one could gain the
ability of programming without having it.

Also, specifically about invariants, pre-/post-conditions, and in
general Hoare logic and program proofs, this is not something specific to
OOP.  It should have been learned along with the procedural programming
languages.  Actually, OO introduces indeed some complexities, of which
the Lyskov's substitution principle is the least, and (purely) functional
programming also does better with a different variant, given that it
avoids mutation.  Lyskov's substitution principle is but a trivial
application of Hoare logic in the context of inheritance.

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

[toc] | [prev] | [next] | [standalone]


#243

FromJon Rossen <jonr17@comcast.net>
Date2015-11-28 17:37 -0800
Message-ID<n3dktf$4ks$1@news.albasani.net>
In reply to#242
On 11/28/2015 4:22 AM, Pascal J. Bourguignon wrote:
> Jon Rossen <jonr17@comcast.net> writes:
>
>> On 11/22/2015 5:02 PM, Pascal J. Bourguignon wrote:
>>> Jon Rossen <jonr17@comcast.net> writes:
>>>
>>>> Thanks for the explanations and to be honest I don't fully understand
>>>> all of what you have said, but I understand it enough for it to make
>>>> sense.  However, I realize I need to understand class invariants which
>>>> is a new concept to me.  I think that is the primary component or
>>>> background for me that is lacking and when I get a better
>>>> understanding I'll fully understand your explanation.
>>>
>>> Have a look at:
>>>
>>> https://en.wikipedia.org/wiki/Liskov_substitution_principle
>>>
>>>
>>
>> Geez, interesting but did you really think someone who calls himself a
>> relative beginner that stated he needed help understanding class
>> invariants could learn about them by reading this?
>
> Of course not.  This is just a pointer to search for further references.
> The newbie shall find a book or some on-line course about the subject.
>
>> You almost need to know all about class invariants *first* in order to
>> understand this Wikipedia article.  Around the time I would start to
>> sort things out I'd get another obscure set of factoids and jargon
>> thrown at me that would create a new set of uncertainty and eventually
>> I spiraled down the rabbit hole of confusion scratching my head and
>> saying WTF!  :-) I'm sure with a few Google searches I may come up
>> with some other stuff to read and perhaps with all of them it will
>> start to become clear.  Thanks for at least taking the time to try and
>> get me on the correct path.
>
> Also I would tend to assume a CS formation for newbie programmers.
> Granted this is probably an error, and also it looks like a lot of
> universities or programming schools fail to teach anything to students
> worth the money they paiy for it, but I fail to see how one could gain the
> ability of programming without having it.
>
> Also, specifically about invariants, pre-/post-conditions, and in
> general Hoare logic and program proofs, this is not something specific to
> OOP.  It should have been learned along with the procedural programming
> languages.  Actually, OO introduces indeed some complexities, of which
> the Lyskov's substitution principle is the least, and (purely) functional
> programming also does better with a different variant, given that it
> avoids mutation.  Lyskov's substitution principle is but a trivial
> application of Hoare logic in the context of inheritance.
>
There was one simple example given that discussed a Rectangle class that 
had a Square sub-class.  Obviously, in Geometry a square is a rectangle 
but a rectangle is not a square.  The programming issue that was brought 
up was basically an outgrowth of this subset/superset relationship 
involving the setters needed to set the dimension of the objects.  For 
rectangles you'd need height and width but for squares you really only 
need one, and furthermore having two for the square would not really be 
correct contextually.  These seem like expected inherent differences in 
these two classes, but I was getting mixed up following the article and 
whether it was suggesting that this condition between these two classes 
were following LSP or 'breaking' it.  Basically I got lost trying to 
fully understand how this example was being used.  Perhaps I'll reread 
that specific section and you as Professor Bourguignon can fill me in 
with answers if I can figure out how to craft some specific questions.  :-)
thx, jonR

[toc] | [prev] | [next] | [standalone]


#244

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-29 03:45 +0100
Message-ID<87fuzpseus.fsf@kuiper.lan.informatimago.com>
In reply to#243
Jon Rossen <jonr17@comcast.net> writes:

> There was one simple example given that discussed a Rectangle class
> that had a Square sub-class.  Obviously, in Geometry a square is a
> rectangle but a rectangle is not a square.  The programming issue that
> was brought up was basically an outgrowth of this subset/superset
> relationship involving the setters needed to set the dimension of the
> objects.  For rectangles you'd need height and width but for squares
> you really only need one, and furthermore having two for the square
> would not really be correct contextually.  These seem like expected
> inherent differences in these two classes, but I was getting mixed up
> following the article and whether it was suggesting that this
> condition between these two classes were following LSP or 'breaking'
> it.  Basically I got lost trying to fully understand how this example
> was being used.  Perhaps I'll reread that specific section and you as
> Professor Bourguignon can fill me in with answers if I can figure out
> how to craft some specific questions.  :-)

It all depends on the set of invariants and pre-/post- conditions you
choose.

Basically, when  you don't specify them, then you have:

    invariant = true
    all pre-condition = true
    all post-condition = true

and since true => true, anything goes in subclasses.

But also, all bugs are features, of course.

If you have:

    -(int)width;
    -(void)setWidth:(int)aWidth;

then you have nothing.

Ie. you cannot say anything about: ([r setWidth:42],[r width])


If you have:

  @interface Rectangle

    -(int)width;

    -(void)setWidth:(int)aWidth;
       // pre:   h==[self height]
       // post:  aWidth==[self width] && h==[self height]

  @end

and similar for heigth, then you won't be able to have Square with the
invariant [square width]==[square height] as a subclass of Rectangle.


But if you have:

  @interface Rectangle

    -(bool)invariant { return YES; }

    -(int)width;

    -(void)setWidth:(int)aWidth;
       // pre:   true
       // post:  aWidth==[self width]

  @end


then you can have Square as a subclass of Rectangle, with:

  @interface Square

    -(bool)invariant { return [self width]==[self height]; }

    -(void)setWidth:(int)aWidth;
       // pre:   true
       // post:  aWidth==[self width] && aWidth==[self height]

  @end

because:

   true ==> true 
and:
   (aWidth==[self width] && aWidth==[self height]) ==> aWidth==[self width]

therefore the pre-condition of -[Rectangle setWidth:] 
      ==> the pre-condition of -[Square setWidth:] 

and the post-condition of -[Square setWidth:] 
==> the post-condition of -[Rectangle setWidth:] 

and -[Square invariant]
==> -[Rectangle invariant]


so Liskov's substitution principle will let you substitue any Rectangle
instance by a Square instance anywhere.

(Because basically, you are not expecting that [self width]!=[self
height], you are just expecting TRUE (ie. anything)).




Notice that there are two sources of difficulty here:

1- mutation.
2- lack of multiple-inheritance.



Consider the graph:

https://en.wikipedia.org/wiki/Parallelogram#/media/File:Symmetries_of_square.svg

it actually gives an inheriting graph (with the superclasses down),
where some subclasses inherit from multiple superclasses (thus
representing an intersection of the set of values of the superclasses).

Trapezoid        isSubclassOf: IrregularQuadrilateral
IsoceleTrapezoid isSubclassOf: Trapezoid
Parallelogram    isSubclassOf: IrregularQuadrilateral
Kite             isSubclassOf: IrregularQuadrilateral

Rhombus          isSubclassOf: Parallelogram and: Kite
Rectangle        isSubclassOf: Parallelogram and: IsoceleTrapezoid

Square isSubclassOf: Parallelogram and: Rectangle and: Rhombus


If we consider only Square and Rectangle, 
we could have:

  @interface Rectangle
  -(id)initWithWidth:(int)w andHeight:(int)h;  //designated initialiser;
  -(bool)invariant{return [self width]>0 && [self height]>0;}
  -(int)width;
  -(int)height;
  // no setters, no mutation
  @end

  @interface Square
  -(id)initWithSide:(int)side; //designated initialiser;
  -(bool)invariant{return [super invariant] && [self width]==[self height];}
  -(int)width;
  -(int)height;
  // no setters, no mutation
  @end

  @implementation Rectangle
  -(id)clusterInitWithWidth:(int)w andHeight:(int)h{
     if((self=[super init])){
        width=w;
        height=h;
     }
     return self;
  }
  -(id)initWithWidth:(int)w andHeight:(int)h{
     w=max(1,w);
     h=max(1,h);
     if(w==h){
        [self dealloc];
        self=[[Square alloc]initWithSide:w];
     }else{
        self=[self clusterInitWithWidth:w andHeight:h];
     }
     return self;
  }
  @end 

  @implementation Square
  -(id)initWithSide:(int)s
     return [self clusterInitWithWidth:s andHeight:s];
  }
  @end 


So now, when you do: 

   [Rectangle initWithWidth:3 andHeight:3]

you get actually an instance of Square, which is a subclass of
Rectangle, so all the method of Rectangle still apply (thanks to
Liskov's Substitution Principle).

(Notice that in Foundation and AppKit, there are several such instances
of class clusters, where allocating a superclass actually gives you an
instance of a specific subclass)

But since in Objective-C we lack multiple-inheritance, this using
immutable classes goes only so far, since we couldn't make Square also a
subclass of Rhombus and Parallelogram.


Types are often considered as mathematical sets of values.

On the other hand, classes, while having a potential set of instances,
define more a representation than a set of values.

For example, we could have two unrelated classes:

    @interface Rectangle
    -(id)initWithWidth:(int)w andHeight:(int)h;
    -(int)width;
    -(int)height;
    @end

    @interface Square
    -(id)initWithSide:(int)s;
    -(int)side;
    @end


    id r=[[Rectangle alloc]initWithWidth:3 andHeight:3];
    id s=[[Square alloc]initWithSide:3];

What is r?

You'll say it's an instance of Rectangle, 
but it's also the representation of a square of side 3.

Just like s, is an instance of Square, and the representation of the
same square of side 3.

So we have two representations of a square of side 3.

Mathematically, there would be a single type, a single set of a square
of side 3, with this unique element, but in OO, we have defined two
different classes and two different instances representating this same
value.

So one should not confuse types and classes, and the set operations that
are mathematically valid on types, are not directly applicable to
classes and their instances.


Having two unrelated classes for Square and Rectangle can be as
problematic has having one class being a subclass of the other or
vice-versa.  You have to choose what representations are the best for
the problem at hand, by specifying the invariants, pre-/post- conditions
that makes you program simple and clear, and therefore the class
hierarchy that works best for the current problem.

For example, if you have to deal with polygons and their mathematical
properties, it might be better to have a general Polygon class, allowing
for any number of points at any coordinates, and instead of representing
the mathematical sets Square Rectangle, Rhombus, Parallelograms,
Triangle, Hexagon, etc,  you could define:

    @interface Polygon
    -(id)initWithPoints:(NSArray*)points;
    -(bool)invariant{return [point count]>=3;}
    -(bool)isRectangle;
    -(bool)isRhombus;
    -(bool)isParallelogram;
    -(bool)isSquare{return [self isParallelogram]
                        && [self isRectangle]
                        && [self isRhombus];}
    …
    -(float)baseAngle;
    …
    @end

So the definition of Square as the intersection of Rectangle
Parallelogram and Rhombus translates clearly as a conjuction in the
predicate -isSquare.

And this model would allow mutation, since you could easily "move" a
single point of the parallelogram (or add or remove points), and the
predicates would "reclassify" it.

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

[toc] | [prev] | [next] | [standalone]


#223

FromDon Bruder <dakidd@sonic.net>
Date2015-11-22 19:15 -0800
Message-ID<n2u08d$7vs$1@dont-email.me>
In reply to#215
In article <n2tcmc$t64$1@news.albasani.net>,
 Jon Rossen <jonr17@comcast.net> wrote:

> I have a rather beginner's basic type of question on designated 
> initializers.  The book that I'm using uses an extremely simple (and 
> trivial) example (which is usually the case) which leaves me with about 
> as many questions as answers.
> 
> Here's the simple example from the book using a Rectangle class which is 
> a child class of NSObject.
> 
> Here's some of the pertinent info from Rectangle.h; I won't bother 
> including stuff that's not directly related to the example:
> 
> Rectangle.h
> 
> @property int width, height;
> 
> -(void) setWidth: (int) w andHeight: (int) h;
> -(instancetype) initWithWidth: (int) w andHeight: (int) h;
> -(instancetype) init;
> 
> 
> Ok, here are just the new, custom init methods from Rectangle.m
> 
> Rectangle.m
> 
> //Designated initializer
> -(instancetype) initWithWidth: (int) w andHeight: (int) h;
> {
>      self = [super init];
>      if (self)
>          [self setWidth:w andHeight:h];
> 
>      return self;
> }
> 
> //The override of super's designated initializer
> -(instancetype) init
> {
>      return [self initWithWidth:0 andHeight:0];
> }
> 
> 
> Ok, I get what's going on here:  You create a new designated initializer 
> which handles a lot of the ivars that you feel are important.  You have 
> to include a call super's designated intializer and assign it to self, 
> then the 'if' test, then your initialization code.  You then override 
> super's designated initializer (in this case it's NSObject's init) and 
> return a call to your designated initializer.  I get how the hierarchy 
> is supposed to work.
> 
> But how is this helpful?  In my mind it's not helpful for a few 
> different reasons.  Reason 1: In the override to init you've hardwired 
> the width and height to be 0.  That's what NSObject's init would do, so 
> it's not doing anything new or better. Reason 2:  Even if you put 
> different values in there say, 1 and 2 or whatever values you thought 
> you might need a newly created Rectangle object to have, they are still 
> hardwired values, buried inside the newly created init method.  How is 
> this helpful at all?  You may want to have a bunch of rectangles of all 
> different dimensions.  At that point you'd have to change their 
> dimensions with the proper accessor method.  If so, why even both with 
> this newly created init when you can just use NSObject's init and use 
> the accessor method to tweak the height and width you want each 
> Rectangle object to be?
> 
> Am I correct that the trivial nature of this example sort of gets in the 
> way of truly understanding the benefit or need of all of this?
> thanks,
> jonR

In a nutshell, the "designated initializer" is *THE* initializer for a 
class that handles *ALL* possibilities for initialization that are 
available - Everything else is convenience methods.

Ferinstance:

Assume you've got a class with 5 instance variables, A, B, C, D, and E

Your "designated initializer" should then look something like this:

-(MyClass *)initWithA:(id)a andB:(id)b andC:(id)c andD:(id)d andE:(id)e
{
 // I'm ignoring the various checking and calls to super's init for
 // brevity, since you seem to understand the chaining mechanism.
 // Now stuff the a,b,c,d, and e instance variables with the appropriate
 // values that were passed in - either by direct assignment, or via
 // accessor methods, as you please - then return self.
}

Now, assuming there's some reason you only need to init an instance 
with, ferinstance, a, while all the other instance vars can be left at 
default values, you could write:

-(MyClass *)initWithJustA:(id)a
{ // (beware line-wrapping for post...)
   return [InitWithA:a andB:defaultBValue andC:defaultCValue 
andD:defaultDValue andE:defaultEValue];
}

Likewise, if you need to init A and B, but C, D, and E can be left at 
default, you'd write:

-(MyClass *)initWithA:(id)a andB:(id)b
{ // (beware line-wrapping for post...)
   return [initWithA:a andB:b andC:defaultCValue andD:defaultDValue 
andE:defaultEValue];
}

and so on for each combination of initializations that make sense for 
your code.

And finally, for an instance of the class that can be left with all of 
the vars at default values, you could write:

-(MyClass *)init
{
   return [InitWithA:defaultAValue andB:defaultBValue andC:defaultCValue 
andD:defaultDValue andE:defaultEValue];
}

Notice how the "plain" init calls the designated initializer with 
default values for each and every instance variable?

Further note that in the "not designated" initializers, you *DON'T* do 
the [super init] checks and calls - You've written the "designated" 
initializer so that it handles doing that. So long as you call the 
designated initializer to do the actual initialization, that stuff gets 
handled "automagically" 

The whole point of the "designated initializer" is that an instance of a 
class - any class - is expected to be "ready to use" immediately after 
being initialized. Using a designated initializer makes certain that any 
setup an instance needs done gets done, with nothing being forgotten.

-- 
Security provided by Mssrs Smith and/or Wesson. Brought to you by the letter Q

[toc] | [prev] | [next] | [standalone]


#224

FromJon Rossen <jonr17@comcast.net>
Date2015-11-23 18:44 -0800
Message-ID<n30iuo$c4b$1@news.albasani.net>
In reply to#223
On 11/22/2015 7:15 PM, Don Bruder wrote:
> In article <n2tcmc$t64$1@news.albasani.net>,
>   Jon Rossen <jonr17@comcast.net> wrote:

>> But how is this helpful?  In my mind it's not helpful for a few
>> different reasons.  Reason 1: In the override to init you've hardwired
>> the width and height to be 0.  That's what NSObject's init would do, so
>> it's not doing anything new or better. Reason 2:  Even if you put
>> different values in there say, 1 and 2 or whatever values you thought
>> you might need a newly created Rectangle object to have, they are still
>> hardwired values, buried inside the newly created init method.  How is
>> this helpful at all?  You may want to have a bunch of rectangles of all
>> different dimensions.  At that point you'd have to change their
>> dimensions with the proper accessor method.  If so, why even both with
>> this newly created init when you can just use NSObject's init and use
>> the accessor method to tweak the height and width you want each
>> Rectangle object to be?
>>
>> Am I correct that the trivial nature of this example sort of gets in the
>> way of truly understanding the benefit or need of all of this?
>> thanks,
>> jonR
>
> In a nutshell, the "designated initializer" is *THE* initializer for a
> class that handles *ALL* possibilities for initialization that are
> available - Everything else is convenience methods.
>
> Ferinstance:
>
> Assume you've got a class with 5 instance variables, A, B, C, D, and E
>
> Your "designated initializer" should then look something like this:
>
> -(MyClass *)initWithA:(id)a andB:(id)b andC:(id)c andD:(id)d andE:(id)e
> {
>   // I'm ignoring the various checking and calls to super's init for
>   // brevity, since you seem to understand the chaining mechanism.
>   // Now stuff the a,b,c,d, and e instance variables with the appropriate
>   // values that were passed in - either by direct assignment, or via
>   // accessor methods, as you please - then return self.
> }
>
> Now, assuming there's some reason you only need to init an instance
> with, ferinstance, a, while all the other instance vars can be left at
> default values, you could write:
>
> -(MyClass *)initWithJustA:(id)a
> { // (beware line-wrapping for post...)
>     return [InitWithA:a andB:defaultBValue andC:defaultCValue
> andD:defaultDValue andE:defaultEValue];
> }
>
> Likewise, if you need to init A and B, but C, D, and E can be left at
> default, you'd write:
>
> -(MyClass *)initWithA:(id)a andB:(id)b
> { // (beware line-wrapping for post...)
>     return [initWithA:a andB:b andC:defaultCValue andD:defaultDValue
> andE:defaultEValue];
> }
>
> and so on for each combination of initializations that make sense for
> your code.
>
> And finally, for an instance of the class that can be left with all of
> the vars at default values, you could write:
>
> -(MyClass *)init
> {
>     return [InitWithA:defaultAValue andB:defaultBValue andC:defaultCValue
> andD:defaultDValue andE:defaultEValue];
> }
>
> Notice how the "plain" init calls the designated initializer with
> default values for each and every instance variable?
>
> Further note that in the "not designated" initializers, you *DON'T* do
> the [super init] checks and calls - You've written the "designated"
> initializer so that it handles doing that. So long as you call the
> designated initializer to do the actual initialization, that stuff gets
> handled "automagically"
>
> The whole point of the "designated initializer" is that an instance of a
> class - any class - is expected to be "ready to use" immediately after
> being initialized. Using a designated initializer makes certain that any
> setup an instance needs done gets done, with nothing being forgotten.
>
Don, Thanks so much for your response with all of this info; I really 
appreciate it.  It is the perfect combination of theory and practical 
info.  You are right, I sort of understand the chaining mechanism in how 
things unfold in the class hierarchy.  The construct that you 
illustrated here with the default values used by the 'convenience' init 
methods is something I came across earlier today coincidentally, but 
your explanation was much more specific, to the point and not obfuscated 
by vague implications.  I have a few more questions:

1. Just a workflow question:  Re: the 'convenience' init methods that 
may be helpful to get objects accompany the designated one:  Is it 
typical for a developer when building a class to add the 'convenience' 
init methods later on or sort of on an 'as needed' basis depending?  I'm 
not asking this as to what's good or bad, but am assuming it just 
depends on the situation and in your experience how does this usually 
play out?

2. Ok, hopefully this question won't open up a can of worms.  What about 
sub-classes?  I guess this is a two part question:
  a) If there's a sub-class that doesn't particularly need it's own 
class designated initializer, it could just use it's super's designated 
initializer, correct?  If so, is this a common design pattern?
b) If the sub-class *does* need a designated initializer, I suppose it 
could do an override of the super's version and that would be ok. 
However, I would think that any additional initialization would have to 
be done via assessor methods as the method names would have to be the 
same.  Would this be considered bad form to do this?  Or would the best 
course of action would be to write a new designated initializer and not 
do an override?
Last one, slightly unrelated to 2a and 2b but still about relationships 
between classes and sub-classes:
c) At each step down the hierarchy through sub-classes, the 'plain' init 
would have to be overridden and it would have to return the current 
class' designated initializer, correct?  In other words, you'd have to 
re-write init for each sub-class where there is a new designated 
initializer.

thanks, jonR

[toc] | [prev] | [next] | [standalone]


#225

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-24 04:58 +0100
Message-ID<87wpt8ujzb.fsf@kuiper.lan.informatimago.com>
In reply to#224
Jon Rossen <jonr17@comcast.net> writes:

> 1. Just a workflow question:  Re: the 'convenience' init methods that
> may be helpful to get objects accompany the designated one:  Is it
> typical for a developer when building a class to add the 'convenience'
> init methods later on or sort of on an 'as needed' basis depending?
> I'm not asking this as to what's good or bad, but am assuming it just
> depends on the situation and in your experience how does this usually
> play out?

The important criteria is whether the superclass -init establishes the
class invariant or not.


> 2. Ok, hopefully this question won't open up a can of worms.  What
> about sub-classes?  I guess this is a two part question:
>  a) If there's a sub-class that doesn't particularly need it's own
> class designated initializer, it could just use it's super's
> designated initializer, correct?  If so, is this a common design
> pattern?

You can change the designated initializer for each sub*class. 
But since this is not a property checked by the compiler, it would be
very hard for the programmer to track which is the designated
initializer of the current sub*class.

> b) If the sub-class *does* need a designated initializer, I suppose it
> could do an override of the super's version and that would be
> ok. However, I would think that any additional initialization would
> have to be done via assessor methods as the method names would have to
> be the same.  Would this be considered bad form to do this?  Or would
> the best course of action would be to write a new designated
> initializer and not do an override?

There's never a "need" for a specific designated initializer.  You can
always either establish the invariant in the -init method (by setting
good default ivars), or extend the invariant to allow for
"half-initilized" instances.

    @interface Rectangle
    {
        int width;
        int height;
    }
    -(BOOL)isValid;
    -(BOOL)invariant;
    -(void)setWidth:(int)aWidth;
    -(void)setHeight:(int)aHeight;
    @end

    // half-initialized instances:

    @implementation Rectangle
    -(BOOL)isValid{return((0<width)&&(0<height));}
    -(BOOL)invariant{return(![self isValid] || ((0<width)&&(0<height)));}
    -(void)setWidth:(int)aWidth{width=aWidth;}      // post: ![self isValid] || [self invariant]
    -(void)setHeight:(int)aHeight{height=aHeight;}; // post: ![self isValid] || [self invariant]
    @end

    [[[Rectangle alloc]init]invariant] --> YES
    [[[Rectangle alloc]init]isValid]   --> NO

    // default-initialized instances:

    @implementation Rectangle
    -(id)init{if((self=[super init])){width=1;height=1;}return(self);}
    -(BOOL)isValid{return YES;}
    -(BOOL)invariant{return((0<width)&&(0<height));}
    -(void)setWidth:(int)aWidth{if(0<aWidth){width=aWidth;}} 
       // pre:  [self invariant] && good=(0<aWidth)
       // post: [self invariant] && (good => width==aWidth)
    -(void)setHeight:(int)aHeight{if(0<aHeight){height=aHeight;}}
       // pre:  [self invariant] && good=(0<aHeight)
       // post: [self invariant] && (good => height==aHeight)
    @end

    [[[Rectangle alloc]init]invariant] --> YES
    [[[Rectangle alloc]init]isValid]   --> YES


> Last one, slightly unrelated to 2a and 2b but still about
> relationships between classes and sub-classes:
> c) At each step down the hierarchy through sub-classes, the 'plain'
> init would have to be overridden and it would have to return the
> current class' designated initializer, correct?  In other words, you'd
> have to re-write init for each sub-class where there is a new
> designated initializer.

No.  If you don't change the designated initializer, then the superclass
-init that sends the designated initializer message will invoke the
designated initializer method of the current class.  You only need to
write a new -init if it has to do something different to ensure the
class invariant.


   enum {red,green,blue};
   @interface ColoredRectangle
   {
      int color;
   }
   @end

   @implementation ColoredRectangle

   -(id)initWithWidth:(int)w andHeight:(int)h{
       if((self=[super initWithWidth:w andHeight:h])){
          color=red;
       }
       return self;
   }

   -(BOOL)invariant{
        return [super invariant]
                && ((color==red)||(color=green)||(color==blue));}

   @end

   [[[ColoredRectangle alloc]init]invariant] --> YES

   [[ColoredRectangle alloc]init] 
       calls -[Rectangle init]
       which sends -initWithWidht:andHeight: to self
       which calls -[ColoredRectangle initWithWidht:andHeight:]
       which sends -initWithWidht:andHeight: to super
       which calls -[Rectangle initWithWidht:andHeight:]
       which sends -init to super
       which calls -[NSObject init]

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

[toc] | [prev] | [next] | [standalone]


#228

FromJon Rossen <jonr17@comcast.net>
Date2015-11-24 16:42 -0800
Message-ID<n3304v$m0n$1@news.albasani.net>
In reply to#225
On 11/23/2015 7:58 PM, Pascal J. Bourguignon wrote:
> Jon Rossen <jonr17@comcast.net> writes:
>
>> 1. Just a workflow question:  Re: the 'convenience' init methods that
>> may be helpful to get objects accompany the designated one:  Is it
>> typical for a developer when building a class to add the 'convenience'
>> init methods later on or sort of on an 'as needed' basis depending?
>> I'm not asking this as to what's good or bad, but am assuming it just
>> depends on the situation and in your experience how does this usually
>> play out?
>
> The important criteria is whether the superclass -init establishes the
> class invariant or not.

Ok, got it, thanks.


>> 2. Ok, hopefully this question won't open up a can of worms.  What
>> about sub-classes?  I guess this is a two part question:
>>   a) If there's a sub-class that doesn't particularly need it's own
>> class designated initializer, it could just use it's super's
>> designated initializer, correct?  If so, is this a common design
>> pattern?
>
> You can change the designated initializer for each sub*class.
> But since this is not a property checked by the compiler, it would be
> very hard for the programmer to track which is the designated
> initializer of the current sub*class.

Ok.

>> b) If the sub-class *does* need a designated initializer, I suppose it
>> could do an override of the super's version and that would be
>> ok. However, I would think that any additional initialization would
>> have to be done via assessor methods as the method names would have to
>> be the same.  Would this be considered bad form to do this?  Or would
>> the best course of action would be to write a new designated
>> initializer and not do an override?
>
> There's never a "need" for a specific designated initializer.  You can
> always either establish the invariant in the -init method (by setting
> good default ivars), or extend the invariant to allow for
> "half-initilized" instances.
>
>      @interface Rectangle
>      {
>          int width;
>          int height;
>      }
>      -(BOOL)isValid;
>      -(BOOL)invariant;
>      -(void)setWidth:(int)aWidth;
>      -(void)setHeight:(int)aHeight;
>      @end
>
>      // half-initialized instances:
>
>      @implementation Rectangle
>      -(BOOL)isValid{return((0<width)&&(0<height));}
>      -(BOOL)invariant{return(![self isValid] || ((0<width)&&(0<height)));}
>      -(void)setWidth:(int)aWidth{width=aWidth;}      // post: ![self isValid] || [self invariant]
>      -(void)setHeight:(int)aHeight{height=aHeight;}; // post: ![self isValid] || [self invariant]
>      @end
>
>      [[[Rectangle alloc]init]invariant] --> YES
>      [[[Rectangle alloc]init]isValid]   --> NO
>
>      // default-initialized instances:
>
>      @implementation Rectangle
>      -(id)init{if((self=[super init])){width=1;height=1;}return(self);}
>      -(BOOL)isValid{return YES;}
>      -(BOOL)invariant{return((0<width)&&(0<height));}
>      -(void)setWidth:(int)aWidth{if(0<aWidth){width=aWidth;}}
>         // pre:  [self invariant] && good=(0<aWidth)
>         // post: [self invariant] && (good => width==aWidth)
>      -(void)setHeight:(int)aHeight{if(0<aHeight){height=aHeight;}}
>         // pre:  [self invariant] && good=(0<aHeight)
>         // post: [self invariant] && (good => height==aHeight)
>      @end
>
>      [[[Rectangle alloc]init]invariant] --> YES
>      [[[Rectangle alloc]init]isValid]   --> YES

Ok, it's going to take me a while to digest this.
>
>
>> Last one, slightly unrelated to 2a and 2b but still about
>> relationships between classes and sub-classes:
>> c) At each step down the hierarchy through sub-classes, the 'plain'
>> init would have to be overridden and it would have to return the
>> current class' designated initializer, correct?  In other words, you'd
>> have to re-write init for each sub-class where there is a new
>> designated initializer.
>
> No.  If you don't change the designated initializer, then the superclass
> -init that sends the designated initializer message will invoke the
> designated initializer method of the current class.  You only need to
> write a new -init if it has to do something different to ensure the
> class invariant.

I'm not sure whether I wasn't clear in how I posed the question and you 
are misunderstanding me, or I'm not understanding your answer.  I asked 
if you had to rewrite init where there is a new designated initializer, 
meaning that you *HAD* to re-write init.  Conversely, when I posed the 
question, my assumption was that if there was NOT a new designated 
initializer then init would not have to be rewritten...which is what you 
seem to be saying here.  So, I'm not sure why you started your answer 
with a 'No':  It either could be that I wasn't clear in my question OR 
you did understand the question and I'm not understanding a fine point 
you are making.

>
>     enum {red,green,blue};
>     @interface ColoredRectangle
>     {
>        int color;
>     }
>     @end
>
>     @implementation ColoredRectangle
>
>     -(id)initWithWidth:(int)w andHeight:(int)h{
>         if((self=[super initWithWidth:w andHeight:h])){
>            color=red;
>         }
>         return self;
>     }
>
>     -(BOOL)invariant{
>          return [super invariant]
>                  && ((color==red)||(color=green)||(color==blue));}
>
>     @end
>
>     [[[ColoredRectangle alloc]init]invariant] --> YES
>
>     [[ColoredRectangle alloc]init]
>         calls -[Rectangle init]
>         which sends -initWithWidht:andHeight: to self
>         which calls -[ColoredRectangle initWithWidht:andHeight:]
>         which sends -initWithWidht:andHeight: to super
>         which calls -[Rectangle initWithWidht:andHeight:]
>         which sends -init to super
>         which calls -[NSObject init]
>
This makes sense, at least the way the method calls work with each 
other.  Did you show this *just* to show the chain of events? Or, did 
you mean it to represent a workflow, in how these methods would be used? 
That's the part that doesn't make sense (using init).   You are starting 
off by calling init, (which calls Rectangle's init) and sets of this 
chain of events.  However, you've lost your opportunity to directly use 
the designated initializer and pass arguments to it.  Wouldn't you just 
use the designated initializer in the sub-class (ColoredRectangle) just 
as it would be used in the parent class (Rectangle)?

[toc] | [prev] | [next] | [standalone]


#230

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2015-11-25 02:00 +0100
Message-ID<87k2p6vqoa.fsf@kuiper.lan.informatimago.com>
In reply to#228
Jon Rossen <jonr17@comcast.net> writes:

> On 11/23/2015 7:58 PM, Pascal J. Bourguignon wrote:
>> Jon Rossen <jonr17@comcast.net> writes:
>>> Last one, slightly unrelated to 2a and 2b but still about
>>> relationships between classes and sub-classes:
>>> c) At each step down the hierarchy through sub-classes, the 'plain'
>>> init would have to be overridden and it would have to return the
>>> current class' designated initializer, correct?  In other words, you'd
>>> have to re-write init for each sub-class where there is a new
>>> designated initializer.
>>
>> No.  If you don't change the designated initializer, then the superclass
>> -init that sends the designated initializer message will invoke the
>> designated initializer method of the current class.  You only need to
>> write a new -init if it has to do something different to ensure the
>> class invariant.
>
> I'm not sure whether I wasn't clear in how I posed the question and
> you are misunderstanding me, or I'm not understanding your answer.  I
> asked if you had to rewrite init where there is a new designated
> initializer, meaning that you *HAD* to re-write init.  Conversely,
> when I posed the question, my assumption was that if there was NOT a
> new designated initializer then init would not have to be
> rewritten...which is what you seem to be saying here.  So, I'm not
> sure why you started your answer with a 'No':  It either could be that
> I wasn't clear in my question OR you did understand the question and
> I'm not understanding a fine point you are making.

Yes, I may have misunderstood.

Indeed, there are two cases:

- the new class defines a new designated initializer
  (then it must override all the other initializers so they all call the
  new designated initializer),

- the new class doesn't define a new designated initializer
  (then it must only override the designated initializer, and can rely
  on the superclasses to forward the other initializers to the
  designated initializer).


However, in the later case, you might still want to override the other
initializers, so that you may call the designated initializer with
default values specific to the new class.


>>     [[ColoredRectangle alloc]init]
>>         calls -[Rectangle init]
>>         which sends -initWithWidht:andHeight: to self
>>         which calls -[ColoredRectangle initWithWidht:andHeight:]
>>         which sends -initWithWidht:andHeight: to super
>>         which calls -[Rectangle initWithWidht:andHeight:]
>>         which sends -init to super
>>         which calls -[NSObject init]
>>
> This makes sense, at least the way the method calls work with each
> other.  Did you show this *just* to show the chain of events? Or, did
> you mean it to represent a workflow, in how these methods would be
> used? That's the part that doesn't make sense (using init).   You are
> starting off by calling init, (which calls Rectangle's init) and sets
> of this chain of events.  However, you've lost your opportunity to
> directly use the designated initializer and pass arguments to it.
> Wouldn't you just use the designated initializer in the sub-class
> (ColoredRectangle) just as it would be used in the parent class
> (Rectangle)?

Of course. If you want to make a new colored rectangle with a different
initial size, you will call the designated initializer.

    [[ColoredRectangle alloc]initWithWidht:42 andHeight:33]
        which sends -initWithWidht:andHeight: to self
        which calls -[ColoredRectangle initWithWidth:andHeight:]
        which sends -initWithWidth:andHeight: to super
        which calls -[Rectangle initWithWidth:andHeight:]
        which sends -init to super
        which calls -[NSObject init]

(But in actual code, you would probably make a new designated
initializer for a colored rectangle:

    @implementation ColorRectangle

        // new designated initializer:
        -(id)initWithWidth:(int)w andHeight:(int)h andColor:(int)c {
             if((self=[super initWithWidht:w andHeight:h])){
                color=c;
             }
             return self;
        }

        //inherited initializers:
        -(id)initWithWidth:(int)w andHeight:(int)h {
            return [self initWithWidth:w andHeight:h andColor:red];
        }

        // for init, you can rely on -[Rectangle init]
        // to call -initWithWidth:andHeight:.

    @end

)
-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

[toc] | [prev] | [next] | [standalone]


#235

FromGreg Parker <gparker@apple.com>
Date2015-11-25 01:25 -0800
Message-ID<sv4k2p62zyn.fsf@xenon.stanford.edu>
In reply to#225
"Pascal J. Bourguignon" <pjb@informatimago.com> writes:

> Jon Rossen <jonr17@comcast.net> writes:
>
>> 2. Ok, hopefully this question won't open up a can of worms.  What
>> about sub-classes?  I guess this is a two part question:
>>  a) If there's a sub-class that doesn't particularly need it's own
>> class designated initializer, it could just use it's super's
>> designated initializer, correct?  If so, is this a common design
>> pattern?
>
> You can change the designated initializer for each sub*class. 
> But since this is not a property checked by the compiler, it would be
> very hard for the programmer to track which is the designated
> initializer of the current sub*class.

Recent versions of clang support compile-time enforcement of the 
designated initializer pattern in initializer calls and overrides. 
Look up __attribute__((objc_designated_initializer)) and its Cocoa 
equivalent NS_DESIGNATED_INITIALIZER.


-- 
Greg Parker     gparker@apple.com     Runtime Wrangler

[toc] | [prev] | [next] | [standalone]


#226

FromDon Bruder <dakidd@sonic.net>
Date2015-11-24 07:38 -0800
Message-ID<n32058$rkn$2@dont-email.me>
In reply to#224
In article <n30iuo$c4b$1@news.albasani.net>,
 Jon Rossen <jonr17@comcast.net> wrote:

> On 11/22/2015 7:15 PM, Don Bruder wrote:
> > In article <n2tcmc$t64$1@news.albasani.net>,
> >   Jon Rossen <jonr17@comcast.net> wrote:
> 
> >> But how is this helpful?  In my mind it's not helpful for a few
> >> different reasons.  Reason 1: In the override to init you've hardwired
> >> the width and height to be 0.  That's what NSObject's init would do, so
> >> it's not doing anything new or better. Reason 2:  Even if you put
> >> different values in there say, 1 and 2 or whatever values you thought
> >> you might need a newly created Rectangle object to have, they are still
> >> hardwired values, buried inside the newly created init method.  How is
> >> this helpful at all?  You may want to have a bunch of rectangles of all
> >> different dimensions.  At that point you'd have to change their
> >> dimensions with the proper accessor method.  If so, why even both with
> >> this newly created init when you can just use NSObject's init and use
> >> the accessor method to tweak the height and width you want each
> >> Rectangle object to be?
> >>
> >> Am I correct that the trivial nature of this example sort of gets in the
> >> way of truly understanding the benefit or need of all of this?
> >> thanks,
> >> jonR
> >
> > In a nutshell, the "designated initializer" is *THE* initializer for a
> > class that handles *ALL* possibilities for initialization that are
> > available - Everything else is convenience methods.
> >
> > Ferinstance:
> >
> > Assume you've got a class with 5 instance variables, A, B, C, D, and E
> >
> > Your "designated initializer" should then look something like this:
> >
> > -(MyClass *)initWithA:(id)a andB:(id)b andC:(id)c andD:(id)d andE:(id)e
> > {
> >   // I'm ignoring the various checking and calls to super's init for
> >   // brevity, since you seem to understand the chaining mechanism.
> >   // Now stuff the a,b,c,d, and e instance variables with the appropriate
> >   // values that were passed in - either by direct assignment, or via
> >   // accessor methods, as you please - then return self.
> > }
> >
> > Now, assuming there's some reason you only need to init an instance
> > with, ferinstance, a, while all the other instance vars can be left at
> > default values, you could write:
> >
> > -(MyClass *)initWithJustA:(id)a
> > { // (beware line-wrapping for post...)
> >     return [InitWithA:a andB:defaultBValue andC:defaultCValue
> > andD:defaultDValue andE:defaultEValue];
> > }
> >
> > Likewise, if you need to init A and B, but C, D, and E can be left at
> > default, you'd write:
> >
> > -(MyClass *)initWithA:(id)a andB:(id)b
> > { // (beware line-wrapping for post...)
> >     return [initWithA:a andB:b andC:defaultCValue andD:defaultDValue
> > andE:defaultEValue];
> > }
> >
> > and so on for each combination of initializations that make sense for
> > your code.
> >
> > And finally, for an instance of the class that can be left with all of
> > the vars at default values, you could write:
> >
> > -(MyClass *)init
> > {
> >     return [InitWithA:defaultAValue andB:defaultBValue andC:defaultCValue
> > andD:defaultDValue andE:defaultEValue];
> > }
> >
> > Notice how the "plain" init calls the designated initializer with
> > default values for each and every instance variable?
> >
> > Further note that in the "not designated" initializers, you *DON'T* do
> > the [super init] checks and calls - You've written the "designated"
> > initializer so that it handles doing that. So long as you call the
> > designated initializer to do the actual initialization, that stuff gets
> > handled "automagically"
> >
> > The whole point of the "designated initializer" is that an instance of a
> > class - any class - is expected to be "ready to use" immediately after
> > being initialized. Using a designated initializer makes certain that any
> > setup an instance needs done gets done, with nothing being forgotten.
> >
> Don, Thanks so much for your response with all of this info; I really 
> appreciate it.  It is the perfect combination of theory and practical 
> info.  You are right, I sort of understand the chaining mechanism in how 
> things unfold in the class hierarchy.  The construct that you 
> illustrated here with the default values used by the 'convenience' init 
> methods is something I came across earlier today coincidentally, but 
> your explanation was much more specific, to the point and not obfuscated 
> by vague implications.  I have a few more questions:
> 
> 1. Just a workflow question:  Re: the 'convenience' init methods that 
> may be helpful to get objects accompany the designated one:  Is it 
> typical for a developer when building a class to add the 'convenience' 
> init methods later on or sort of on an 'as needed' basis depending?  I'm 
> not asking this as to what's good or bad, but am assuming it just 
> depends on the situation and in your experience how does this usually 
> play out?

I can't speak for anyone but myself, so YMMV...
Personally, I code a "designated" initializer that takes appropriate 
inputs and uses them to handle *EVERYTHING*, to start with, then a 
"naked init" ("-(MyClass *)init") method that passes some reasonable 
defaults into the designated initializer. Often, but nowhere near 
always, that's all that's needed. (Or more accurately, it's *ALWAYS* all 
that's *NEEDED*, but it's only occasionally all that's *WANTED*, if you 
understand the distinction) If, as the project progresses, and I notice 
a need for them, I might add more convenience initializers. The decision 
to add more convenience initializers or not is totally dependent on the 
needs of the specific project - In one project, I might code the 
designated initializer, the cover for "init", and several variations, as 
needed. In another, I might not code anything except the designated 
initializer, and a cover for "plain" init so that any subclasses I (or 
someone else...) might cook up later will chain back up the line 
properly when hit with a "[super init]". Either way, if somebody 
subclasses from your class, they come in with the expectation that doing 
"[super init]" will do the right thing (by calling the root object's 
init method - perhaps directly, or perhaps through a very long and 
complex chain of calls, but it'll happen eventually)

> 
> 2. Ok, hopefully this question won't open up a can of worms.  What about 
> sub-classes?  I guess this is a two part question:
>   a) If there's a sub-class that doesn't particularly need it's own 
> class designated initializer, it could just use it's super's designated 
> initializer, correct?  If so, is this a common design pattern?

At least in theory, yes, that's correct. As for how common it is... 
<shrug> No clue - I've never actually taken the time to think about it. 

(minor nitpick: "it's" is a contraction meaning "it is" - You're looking 
for the possessive form: "its" - "that which belongs to it". Ain't it 
wonderful how consistent the english language is? :) )

> b) If the sub-class *does* need a designated initializer, I suppose it 
> could do an override of the super's version and that would be ok. 
> However, I would think that any additional initialization would have to 
> be done via assessor methods as the method names would have to be the 
> same.

Sounds right... I THINK - Assuming you're asking what I think you are.

If you're expecting to be subclassing off your subclass, then write your 
"first level" subclass - let's call it MyClass - like so:
(I'm assuming you're on a Mac, so you'll probably be subclassing it from 
NSObject)

Your .h file has (at least - perhaps MUCH more) in it:
---cut---
#import <Cocoa/Cocoa.h>

@interface MyClass : NSObject 
{
   // MyClass specific vars
   NSDictionary *myClassVars;
}
// And some method declarations
-(MyClass *)designatedInitializer:(id)anyNeededParams;
-(MyClass *)init;
// More stuff will likely live here in a useful program.
@end
---cut---

And in your .m file:
---cut---
#import "MyClass.h"
@implementation MyClass

-(MyClass *)designatedInitializer:(id)anyNeededParams
{
   self = [super init]; // Invoke NSObject's init
   
   // Do MyClass-specific init

   // and return what self has become.
   return self;
}

-(MyClass *)init
{
   // I'll assume defaultParams is set appropriately elsewhere
   return [self designatedInitializer:defaultParams];
}

// Any other methods MyClass needs follow
@end
---cut---

Notice that all init does is call the instance's designated initializer 
with default params. Which in turn calls [super init] (NSObject's, in 
this case)  before using the parameters it gets passed. You get the rest 
of your MyClass going, ship the project, and make a few bucks.

Time passes, and a project you just got handed wants something very 
nearly identical to MyClass, but with a couple little additions and 
tweaks. You decide to subclass from MyClass to make a new class of 
object -  The MySubclass

You toss together a quick .h file:
---cut---
#import "MyClass.h"
@interface MySublass : MyClass
{
   // MySublass specific vars
   NSDictionary *SubclassVar;
}
// And don't forget the method declarations
-(MyClass *)designatedInitializer:(id)anyNeededParams;
-(MyClass *)init;

@end
---cut---

And the .m file
---cut---
#import "MySubclass.h"
-(MySubclass *)designatedInitializer:(id)anyNeededParams
{
   self = [super init]; // Invokes MyClass' init
   
   // Do MySubclass-specific init work

   // and return what self has become.
   return self;
}

-(MySubclass *)init
{
   // I'll assume defaultMySubclassParams is set appropriately somewhere
   return [self designatedInitializer:defaultMySubclassParams];
}

// Any other methods MySubclass needs follow

@end
---cut---

You'll notice how close to identical the code is for both cases - The 
critical consideration is that you ALWAYS want your class' "init" to 
chain back to the root object, whether directly or indirectly.

> c) At each step down the hierarchy through sub-classes, the 'plain' init 
> would have to be overridden and it would have to return the current 
> class' designated initializer, correct?  In other words, you'd have to 
> re-write init for each sub-class where there is a new designated 
> initializer.

Basically, yes. See the snippets above.

-- 
Security provided by Mssrs Smith and/or Wesson. Brought to you by the letter Q

[toc] | [prev] | [next] | [standalone]


#227

FromJon Rossen <jonr17@comcast.net>
Date2015-11-24 15:54 -0800
Message-ID<n32tc0$hik$1@news.albasani.net>
In reply to#226
< A bunch of previous stuff cut out...new comments inline below>
thx -jonR

>> Don, Thanks so much for your response with all of this info; I really
>> appreciate it.  It is the perfect combination of theory and practical
>> info.  You are right, I sort of understand the chaining mechanism in how
>> things unfold in the class hierarchy.  The construct that you
>> illustrated here with the default values used by the 'convenience' init
>> methods is something I came across earlier today coincidentally, but
>> your explanation was much more specific, to the point and not obfuscated
>> by vague implications.  I have a few more questions:
>>
>> 1. Just a workflow question:  Re: the 'convenience' init methods that
>> may be helpful to get objects accompany the designated one:  Is it
>> typical for a developer when building a class to add the 'convenience'
>> init methods later on or sort of on an 'as needed' basis depending?  I'm
>> not asking this as to what's good or bad, but am assuming it just
>> depends on the situation and in your experience how does this usually
>> play out?
>
> I can't speak for anyone but myself, so YMMV...
> Personally, I code a "designated" initializer that takes appropriate
> inputs and uses them to handle *EVERYTHING*, to start with, then a
> "naked init" ("-(MyClass *)init") method that passes some reasonable
> defaults into the designated initializer. Often, but nowhere near
> always, that's all that's needed. (Or more accurately, it's *ALWAYS* all
> that's *NEEDED*, but it's only occasionally all that's *WANTED*, if you
> understand the distinction) If, as the project progresses, and I notice
> a need for them, I might add more convenience initializers. The decision
> to add more convenience initializers or not is totally dependent on the
> needs of the specific project - In one project, I might code the
> designated initializer, the cover for "init", and several variations, as
> needed. In another, I might not code anything except the designated
> initializer, and a cover for "plain" init so that any subclasses I (or
> someone else...) might cook up later will chain back up the line
> properly when hit with a "[super init]". Either way, if somebody
> subclasses from your class, they come in with the expectation that doing
> "[super init]" will do the right thing (by calling the root object's
> init method - perhaps directly, or perhaps through a very long and
> complex chain of calls, but it'll happen eventually)

Ok....but backing up to the beginning of your paragraph:
Hmmm, you lost me when you brought up your 'naked init' and said that it 
'passes some reasonable defaults into the designated initializer'.  The 
naked init in the current class just calls the current class' designated 
initializer and returns it.  How does it pass some reasonable defaults 
*into it*??  Might you be referring to the naked init that is the 
*superclass* of the current class?  Certainly the super's init does that 
when you do self = [super init].  I just don't understand how the init 
at the current level feeds anything into the designated initializer at 
that same level.


>> 2. Ok, hopefully this question won't open up a can of worms.  What about
>> sub-classes?  I guess this is a two part question:
>>    a) If there's a sub-class that doesn't particularly need it's own
>> class designated initializer, it could just use it's super's designated
>> initializer, correct?  If so, is this a common design pattern?
>
> At least in theory, yes, that's correct. As for how common it is...
> <shrug> No clue - I've never actually taken the time to think about it.
>
> (minor nitpick: "it's" is a contraction meaning "it is" - You're looking
> for the possessive form: "its" - "that which belongs to it". Ain't it
> wonderful how consistent the english language is? :) )

Yeah, yeah, I know all of that; it was purely a typo due to being tired. 
  When I get tired I have a tendency to spell fenetically :-) and little 
details drop off by the wayside.  But great nitpick...I'd give it a 9 
out of 10 at least.  :-)

Speaking of nitpicks, in your code examples why don't you use 
(instancetype) as your return type?

>
>> b) If the sub-class *does* need a designated initializer, I suppose it
>> could do an override of the super's version and that would be ok.
>> However, I would think that any additional initialization would have to
>> be done via assessor methods as the method names would have to be the
>> same.
>
> Sounds right... I THINK - Assuming you're asking what I think you are.

OK.

> If you're expecting to be subclassing off your subclass, then write your
> "first level" subclass - let's call it MyClass - like so:
> (I'm assuming you're on a Mac, so you'll probably be subclassing it from
> NSObject)


>
> Your .h file has (at least - perhaps MUCH more) in it:
> ---cut---
> #import <Cocoa/Cocoa.h>
>
> @interface MyClass : NSObject
> {
>     // MyClass specific vars
>     NSDictionary *myClassVars;
> }
> // And some method declarations
> -(MyClass *)designatedInitializer:(id)anyNeededParams;
> -(MyClass *)init;
> // More stuff will likely live here in a useful program.
> @end
> ---cut---
>
> And in your .m file:
> ---cut---
> #import "MyClass.h"
> @implementation MyClass
>
> -(MyClass *)designatedInitializer:(id)anyNeededParams
> {
>     self = [super init]; // Invoke NSObject's init
>
>     // Do MyClass-specific init
>
>     // and return what self has become.
>     return self;
> }
>
> -(MyClass *)init
> {
>     // I'll assume defaultParams is set appropriately elsewhere
>     return [self designatedInitializer:defaultParams];
> }
>
> // Any other methods MyClass needs follow
> @end
> ---cut---
>
> Notice that all init does is call the instance's designated initializer
> with default params. Which in turn calls [super init] (NSObject's, in
> this case)  before using the parameters it gets passed. You get the rest
> of your MyClass going, ship the project, and make a few bucks.

Right.  This code example is pretty much the same example you showed me 
the other day, and BTW, is consistent with my understanding of how to do 
this.  So, there's nothing new here with respect to the other day's 
example, right?

>
> Time passes, and a project you just got handed wants something very
> nearly identical to MyClass, but with a couple little additions and
> tweaks. You decide to subclass from MyClass to make a new class of
> object -  The MySubclass
>
> You toss together a quick .h file:
> ---cut---
> #import "MyClass.h"
> @interface MySublass : MyClass
> {
>     // MySublass specific vars
>     NSDictionary *SubclassVar;
> }
> // And don't forget the method declarations
> -(MyClass *)designatedInitializer:(id)anyNeededParams;
> -(MyClass *)init;
>
> @end
> ---cut---
>
> And the .m file
> ---cut---
> #import "MySubclass.h"
> -(MySubclass *)designatedInitializer:(id)anyNeededParams
> {
>     self = [super init]; // Invokes MyClass' init
>
>     // Do MySubclass-specific init work
>
>     // and return what self has become.
>     return self;
> }
>
> -(MySubclass *)init
> {
>     // I'll assume defaultMySubclassParams is set appropriately somewhere
>     return [self designatedInitializer:defaultMySubclassParams];
> }
>
> // Any other methods MySubclass needs follow
>
> @end
> ---cut---
>
> You'll notice how close to identical the code is for both cases - The
> critical consideration is that you ALWAYS want your class' "init" to
> chain back to the root object, whether directly or indirectly.

Not only are they close, aren't they basically *the exact same code* 
structurally?  Meaning apart from the class-specific stuff that would be 
needed (different name of designated initializer, additional parameters, 
etc., etc.), the framework here is exactly the same as it was for the 
subclass (MyClass).  I would *expect* it to be the same seeing that it's 
keeping the init chaining intact.  Am I missing something or am I 
'getting it'?

>
>> c) At each step down the hierarchy through sub-classes, the 'plain' init
>> would have to be overridden and it would have to return the current
>> class' designated initializer, correct?  In other words, you'd have to
>> re-write init for each sub-class where there is a new designated
>> initializer.
>
> Basically, yes. See the snippets above.

Yep, my above two comments discusses this.

thanks for your response.
-jonR

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.objective-c


csiph-web