Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.objective-c > #215 > unrolled thread
| Started by | Jon Rossen <jonr17@comcast.net> |
|---|---|
| First post | 2015-11-22 13:39 -0800 |
| Last post | 2015-11-24 18:29 -0800 |
| Articles | 20 on this page of 32 — 5 participants |
Back to article view | Back to comp.lang.objective-c
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 →
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-11-22 13:39 -0800 |
| Subject | A 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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | Louis Wu <louiswu@ringworld.net> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | Don Bruder <dakidd@sonic.net> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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]
| From | "Pascal J. Bourguignon" <pjb@informatimago.com> |
|---|---|
| Date | 2015-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]
| From | Greg Parker <gparker@apple.com> |
|---|---|
| Date | 2015-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]
| From | Don Bruder <dakidd@sonic.net> |
|---|---|
| Date | 2015-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]
| From | Jon Rossen <jonr17@comcast.net> |
|---|---|
| Date | 2015-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