Field Initialization

Field Initialization

Post by John C. Randolp » Sat, 26 Jul 2003 10:32:37





> > It's not just a few cycles.  Walking a tree with hundreds or thousands
> > of nodes could take a long time on a 33Mhz 68K machine.

> Your strategy of stuffing everything in a zone and than
> subsequently not caring about it any longer and drop the zone is terrible.

In typical usage, when a window was closed (like when a document was
saved) the zone associated with that window would be removed in a single operation.

Quote:> It is medieval, compared what is out there, like Boehm, like -refcnt etc.

If by "medieval" you mean "highly efficient", then you'd be correct.

Quote:> What happens if one of those 'hundreds of thousands of nodes' in your zone,
> happens to have ONE (1) pointer to a piece of memory that is outside the
> zone ?

Nothing.  The objects in the zone go away, so nobody cares what other
objects they may have referenced.

Quote:> What happens if objects from another zone point to objects that were
> brutally destroyed by disposing of the zone ?

That would be a problem, and that's why it's important to keep good
model-view-controller organization when using zones.

Quote:> It is extremely bad programming practice to do this kind of Apple memory
> management with 'zones'; in fact, it's the opposite of good programming style.

It is extremely clear that once again, you don't know what you're
talking about.

-jcr

 
 
 

Field Initialization

Post by jp » Sat, 26 Jul 2003 18:02:44




...
> > What happens if objects from another zone point to objects that were
> > brutally destroyed by disposing of the zone ?

> That would be a problem, and that's why it's important to keep good
> model-view-controller organization when using zones.

Well, let's explore that thought for a moment. Say you have a reusable
object using zones, how would you implement/document -set/-get
methods?

As I see it, either you have two options:

document your weird behaviour, e.g. the return object for a -get is
only alive as long as the zone of the called object is alive. objects
used in -set methods must be either allocated in the same zone as the
called object, or the caller remains responsible for their disposal,
usw.

or

implement all your set/get methods as in

- setXXX:anXXX {

   xxx = [[[anXXX class] allocWith [self zone]] initWith:anXXX];

Quote:}

- getXXX {

   return [[xxx class] alloc] initWith:anXXX];

Quote:}

(i.e. move from the default zone to the called object zone and back
again)

which is all fine, until your objects of interest are container
objects themselves, and you just have opened the can of recursive
cloning.

Anyway this zone shit is even incompatible with the retain/release
stuff.
Retaining an object is no longer a guarantee to keep an object of
interest alive.

jcr's statement

"it's important to keep good model-view-controller organization when
using zones"

is in my opinion an euphemism for

"when using zones, you will have to resort to tight coupling of
objects"

so I have to agree with David here, zones are not only a bad hack that
should have been avoided, but they also weaken the aspect of loosely
coupled reusable objects that is one of the major design criteria for
objective-C.

The usual idiots are going to try to point out that I supposedly don't
know the difference between a language and a framework. Big *ing
difference. If you have a framework that does not encapsulate objects,
in the sense that vanilla objective-C objects can no longer interact
reliably with these framework objects without knowing the semantics of
retain/release and zone management, then you have de facto constructed
a dialect of the language, and no amount of doubletalk is going to
change that. So yes, objective-C with zones is crappy objective-C.

 
 
 

Field Initialization

Post by Michael J As » Sat, 26 Jul 2003 21:29:44




> > Your strategy of stuffing everything in a zone and than
> > subsequently not caring about it any longer and drop the zone is terrible.

> In typical usage, when a window was closed (like when a document was
> saved) the zone associated with that window would be removed in a single operation.

> > It is medieval, compared what is out there, like Boehm, like -refcnt etc.

> If by "medieval" you mean "highly efficient", then you'd be correct.

This discussion is getting pretty bizarre. As far as I can tell, the whole
zones thing is pretty deprecated in Cocoa. Also, I have not seen a way to
drop all objects in a zone. The closest I can see, NSZoneRecycle,
explicitly does not destroy the objects within, but rather adds them to
the default zone.

The one time I thought I had a use for zones, it was so I could track the
amount of memory being used by each part of my program. But, ha ha,
there's no way to ask a zone how much memory it actually contains.

I have to side with Stes on this one; zones are useless. That does *not*
mean the ability to use different types of allocation is useless, though.

--
"From now on, we live in a world where man has walked on the moon.
 And it's not a miracle, we just decided to go." -- Jim Lovell


 
 
 

Field Initialization

Post by John C. Randolp » Sun, 27 Jul 2003 05:50:02



>  Say you have a reusable
> object using zones, how would you implement/document -set/-get
> methods?

You'd implement your access methods the way you always would.  If you're
using zones, it's incumbent on you to ensure that you don't keep any
references to something to that lives in a zone that you might destroy.

-jcr

 
 
 

Field Initialization

Post by Christian Brunsch » Wed, 06 Aug 2003 20:30:32




Quote:

>While not necessarily unsubstantiated, both posters seem to miss the
>point.

I do try not to miss any points :)

One small question, first off: From your message, I would guess that you
were one of the developers of Objective-C at PPI / Stepstone. Is this a
correct interpretation?

Quote:>The early versions of the Stepstone (then PPI) ICpak libraries
>used +new to do allocation of the instance frame, and initialization
>of the frame. When early binding was added to OC 4.0, we needed to
>separate the allocation from the initialization. This allowed the
>allocation to be done by the compiler/postlinker (Note that the
>VAX/VMS version did not use the postlinker, thanks to nifty PSECT
>directives) and init could be called on entry to the instance's scope.

>This was generally a good thing anyway, and reflected the evolution of
>the mechanism in Smalltalk.

>The +new was retained for two reasons:
>    1) backward-compatibility
>    2) as a convenience for creating and initializing dynamically
>allocated instances.

>There is no reason why you should be required to call alloc and init
>separately, but if you want to, go ahead. However, for the purpose for
>which it was intended, +new provides a shortcut.

>As an example of when you would not use +new: if you implement
>instance pooling, or some other allocation scheme, you can simply call
>init on instances as they are added to or removed from the pool.

If I read what you are writing correctly, then you are basically saying
(and please do correct me if my reading is incorrect):

1) Separation of allocation and initialization of objects is a good, and
   sometimes even necessary, thing
2) +new was retained for convenience and backwards compatibility, but
   implemented using the underlying separate allocation and initialization

This is very much what I have been trying to say. In particular, I have
tried to be careful to contrast '+alloc/-init' against a _monolithic_ new
which did not build on an underlying separation between allocation and
initialization. If I haven't managed to bring that across, then I must
obviously polish my communication skills.

Best wishes,

Quote:>cppc

// Christian Brunschen
 
 
 

Field Initialization

Post by Christian Brunsch » Wed, 06 Aug 2003 22:06:55





>> While not necessarily unsubstantiated, both posters seem to miss the
>> point. The early versions of the Stepstone (then PPI) ICpak libraries
>> used +new to do allocation of the instance frame,

>They all use +new, not just the early versions.  The version for AIX
>(the one that was shipped with NextStep for AIX did use +alloc/-init,
>but only because that was needed for compatibility).

>> of the frame. When early binding was added to OC 4.0, we needed to
>> separate the allocation from the initialization.

>This is nonsense.  +new means : create a new instance.  Whether it is
>dynamically allocated or early bound doesn't matter.  The mistake is to
>think in terms of specific mappings, of specific implementations, instead
>of the abstract action : the abstract action of creating an instance.

>You seem to think : +new creates a *dynamically allocated* instance, so we
>need something else for early binding.  The mistake is that it is not
>necessary that +new creates such a dynamically allocated instance, the power
>of the message selector name "new' is precisely that it does not specify
>HOW the instance is created, just that it has to be created, in any appropriate
>way, early bound or not.

The problem is that, as soon as there is more than one way of allocating
an instance, the simplicity of +new also becomes its greatest limitation:
it completely hides the underlying process for creating the instance, but
more importantly, it makes it impossible to easily choose which way of
allocating an instance should be used in any one particular case.

To cater to different ways of allocating an instance - for instance,
whether to malloc() the memory for an instance or whether to get it from a
pre-existing object pool - it is necessary to be able to specify which one
of these instance allocation methods to use. This means that, essentially,
we'd need a +new and a +newFromPool:pool method to offer this
functionality.

Of course, we still have the issue of instance initialization. Consider
now a class which can be initialized in three different ways, either
passing an int, or a double, or a char*. This would generally have been
solved like:

+newWithDouble:(double)d { /* ... */ }
+newWithInt:(int)i       { /* ... */ }
+newWithString:(char *)s { /* ... */ }

However, if we now add the necessity to support creation of objects from a
pool as mentioned above, we now have to have one instance creation method
for each possible combination of allocation and initialization:

+newWithDouble:(double)d               { /* ... */ }
+newWithDouble:(double)d fromPool:pool { /* ... */ }
+newWithInt:(int)i                     { /* ... */ }
+newWithInt:(int)i fromPool:pool       { /* ... */ }
+newWithString:(char *)s               { /* ... */ }
+newWithString:(char *)s fromPool:pool { /* ... */ }

As we can see, we had to add three new +newWith... methods, one for each
one of the existing ways of initializing an instance, when adding one new
way of allocating an instance's memory.

In contrast, using separate allocation and initliazation methods, we have:

+alloc                    { /* ... */ } /* inherited */

-initWithDouble:(double)d { /* ... */ }
-initWithInt:(int)i       { /* ... */ }
-initWithString:(char *)s { /* ... */ }

And to add a new allocation method we just have to write the allocation
method itself:

+allocFromPool:pool       { /* ... */ }

Already with a small class as this one, we see that the separate
allocation and initialization methods, which we can easily combine, lead
to fewer methods having to be written compared to the case where there is
a +new... method covering each separate case.

Now consider if you have a class hierarchy with dozens of classes, each
with several different ways of initialization ... and you want to add a
facility to allocate these instances from a pool of pre-fetched memory.  
Using the monolithic +new approach, you now have to write literally dozens
of methods, several in each class, and all essentially duplicating
functionality of an existing method with the minor difference that they
call a different [super new...] method than the pre-existing methods.
If you have separate allocation and initialization methods, you only have
to write the new allocation method, once.

Essentially, if you have _x_ ways of allocating objects and _y_ ways of
initializing them, then using a monolithic approach where the instance
creation method has to both allocate and initialize the instance, will
lead to a total of _x * y_ methods being written to cater for all the
possible combinations; of instead you separate the process into separate
allocation and initialization methods which you can combine as you wish,
you end up with a total of _x + y_ methods (_x_ allocation methods and _y_
initialization methods), while offering the same functionality.

Of course, even with the approach of separate allocation and
initialization methods, you can still write cover methods for any of the
combinations which you want to have more convenient and easier-to-type
access to - for instance, you can always use +new as a cover method for
[[someClass alloc] init]. So, there is nothing wrong with the _message_
+new as a way to create an instance of an object; the issue lies with
how the method +new is implemented, whether in a monolithic fashion or in
a separated fashion. The monolithic fashion forces code duplication among
almost-identical object creation methods in the face of having to cater
for varying combinations of object allocation and initialization; the
separate fashion allows these combinations to be expressed in their
natural way, as combinations of two different steps, and only requires
each one of those steps to be written, while still allowing for convenient
access to those combinations we use extra frequently.

All in all, I consider the approach of having instance allocation and
instance initialization implemented and accessible as separate steps,
and particularly as separate methods, to be vastly superior to the
monolithic approach.

Best wishes,

// Christian

 
 
 

Field Initialization

Post by Christopher Caseri » Thu, 07 Aug 2003 10:23:15





>>While not necessarily unsubstantiated, both posters seem to miss the
>>point.

>I do try not to miss any points :)

I didn't mean it in a * or personal way...

Quote:>One small question, first off: From your message, I would guess that you
>were one of the developers of Objective-C at PPI / Stepstone. Is this a
>correct interpretation?

Yes, it is. The version of OC delivered to NeXT was designed and
developed by a three person team, of which I was one. The most
relevant aspect of which was the design for "static binding", which
whould allow a declaration of an instance within the C allocation
scope. So you could write:

Fruit m;

rather than:

id m;

The compiler could then bind methods at link time, using a jump table,
as was eventually used for virtual function in C++.

As some, including Mr. Stes, have pointed out, this was not a feature
that NeXT promoted, although many of our other customers were asking
for this capability. In this scheme, the object instance allocation
was dependent on the C scope in which it was defined, and the compiler
tracked that.

Quote:>>The early versions of the Stepstone (then PPI) ICpak libraries
>>used +new to do allocation of the instance frame, and initialization
>>of the frame. When early binding was added to OC 4.0, we needed to
>>separate the allocation from the initialization. This allowed the
>>allocation to be done by the compiler/postlinker (Note that the
>>VAX/VMS version did not use the postlinker, thanks to nifty PSECT
>>directives) and init could be called on entry to the instance's scope.

>>This was generally a good thing anyway, and reflected the evolution of
>>the mechanism in Smalltalk.

>>The +new was retained for two reasons:
>>    1) backward-compatibility
>>    2) as a convenience for creating and initializing dynamically
>>allocated instances.

>>There is no reason why you should be required to call alloc and init
>>separately, but if you want to, go ahead. However, for the purpose for
>>which it was intended, +new provides a shortcut.

>>As an example of when you would not use +new: if you implement
>>instance pooling, or some other allocation scheme, you can simply call
>>init on instances as they are added to or removed from the pool.

>If I read what you are writing correctly, then you are basically saying
>(and please do correct me if my reading is incorrect):

>1) Separation of allocation and initialization of objects is a good, and
>   sometimes even necessary, thing
>2) +new was retained for convenience and backwards compatibility, but
>   implemented using the underlying separate allocation and initialization

>This is very much what I have been trying to say. In particular, I have
>tried to be careful to contrast '+alloc/-init' against a _monolithic_ new
>which did not build on an underlying separation between allocation and
>initialization. If I haven't managed to bring that across, then I must
>obviously polish my communication skills.

I'm not sure if it's entirely a godd thing, but sometimes it is
necessary. What I said above is why it was done, not any universal
principle of how it should be done by everyone, everywhere. If you did
not use static binding (and actually even if you did), the separation
was hidden by +new, which would allocate from dynamic heap, by
default.

- Show quoted text -

Quote:>Best wishes,

>>cppc

>// Christian Brunschen

 
 
 

Field Initialization

Post by David Ste » Fri, 08 Aug 2003 14:28:12



> Fruit m;

> [...] many of our other customers were asking
> for this capability. In this scheme, the object instance allocation
> was dependent on the C scope in which it was defined, and the compiler
> tracked that.

Static binding may be useful.  But the customers you refer to, are customers
of the Stepstone compiler.

It is only the Stepstone compiler that supports this "Fruit m" syntax.

You will not be able to compile this with Apple's or NeXT's compiler (or POC).

The static binding options (-sBind etc.) are intended to move dynamic lookup
of messages to static lookup (the compiler can see what the IMP for a certain
message would be, the jumptable at compile time that you refer to).

Static binding could be considered a feature that is only in the Stepstone
compiler (in some versions at least, since you will not find a lot about it
in the books on Stepstone objective-c, given the fact that it was only added
later).

Maybe such a feature could be added to other compilers, one could  argue that
this is lacking in other implementations.

But currently it is NOT there in the GCC (derived) compilers.

And it was also not there in the GCC compilers when +alloc/-init was introduced.