[comp.sys.next] [self free] is a bad example

clement@opus.cs.mcgill.ca (Clement Pellerin) (01/12/90)

In the SysRefMan chap. 3 p.27
there is an example on how to use the message free in class Object.
The intent of free is to release the memory taken up by objects
that you allocated with new but you don't need anymore.

The example is [self free].  Note that self will be deallocated by
the time this message returns.  The object class of self will not be deallocated
and hence the method will still be available.  The instance variables of
self will be gone.  Should an object that does not exist anymore be
allowed to continue executing a method if it does not access its variables
nor sends a message to itself?

What does the objective C ref manual say about this?

I tried the following program and the variables are kept after [self free]
but you can't send a message to self.  I would prefer if I would get
a run-time error by accessing the variables.

let me propose a different example:

  id anObject;

  anObject = [AClass new];
  // use anObject until you don't need it
  [anObject free];
  // continue execution without anObject

Is it worth contacting NeXT?

--------------------<snip>------------------------<snip>-----------------------
// put me in file selffree.m
// compile me with cc -ObjC -I/usr/include/objc selffree.m -lNeXT_s -lsys_s
// it took me 30mins to find that command, is there a better way?

#import <Object.h>

@interface Suicide:Object {
  int x;
}

- setx: (int)val;
- die;
- sayhi: (int)cnt;

@end

@implementation Suicide:Object;

- setx: (int)val {
  x = val;
}

- die {
  [self free];
  printf("I'm dead but I remember the value of x = %d\n", x);
  [self sayhi: 2];
}

- sayhi: (int)cnt {
  printf("I'm alive %d\n", cnt);
}
@end

main()
{
  Suicide *suicidal;

  suicidal = [Suicide new];
  [suicidal setx: 111];
  [suicidal sayhi: 1];
  [suicidal die];
  [suicidal sayhi: 3];
}
--------------------<snip>------------------------<snip>-----------------------
-- 
news <clement

jacob@gore.com (Jacob Gore) (01/13/90)

/ comp.sys.next / clement@opus.cs.mcgill.ca (Clement Pellerin) / Jan 11, 1990 /
> In the SysRefMan chap. 3 p.27
> there is an example on how to use the message free in class Object. [...]
> The example is [self free].

This is indeed a bad example.  It's technically correct, but it belongs in
something like "Objective-C Traps and Pitfalls", not as an example for the
typical use of -free.

> Note that self will be deallocated by the time this message returns.  The
> object class of self will not be deallocated and hence the method will
> still be available.  The instance variables of self will be gone.

I don't follow this reasoning.  Whether or not the methods of the former
self are still callable is at best implementation-dependent.  For example,
if methods are reached through the 'isa' instance variable (a pointer to
the class struct), and that variable is not available anymore, neither are
the methods.  You may get "lucky" if all pointers just happen to be intact
in memory, or because the method is cached, etc.

In any case, trying to access instance variables or instance methods of a
freed objects is a bad idea.

> Should
> an object that does not exist anymore be allowed to continue executing a
> method if it does not access its variables nor sends a message to itself?

Yes, since it may not access its variables or send messages to itself for
the rest of the execution of the method.

Besides, 'self' is assignable.  One may want to do something like this:

	- finishUnarchiving
	{
		if ([self isInconsistent]) {
			[self free];
			self = [SelfsClass new];
			[self setSomeInstanceVariables];
		}
		return self;
	}

> What does the objective C ref manual say about this?

Don't know, don't have one.

> I tried the following program and the variables are kept after [self free]
> but you can't send a message to self.

I think that's a fluke.

> I would prefer if I would get a run-time error by accessing the variables.

I agree.

> let me propose a different example:
> 
>   id anObject;
> 
>   anObject = [AClass new];
>   // use anObject until you don't need it
>   [anObject free];
>   // continue execution without anObject

Yes, except I'd use "AClass *anObject" instead of "id anObject".  Now that
we finaly have compile-time object typing in Objective-C, let's encourage
people to take advantage of it.

I would also provide an example on how to write the -free method for an
object:

	#import "Something.h"
	@interface Example : Object
	{
	    char *aString;
	    Something *aSomething;
	}

	+ new;
	- free;

	@end

	#import <stdlib.h>
	@implementation Example

	+ new
	{
	    self = [super new];
	    aString = malloc(100);
	    aSomething = [Something new];
	    return self;
	}

	- free {
	    if (aString) free(aString);	// May still fail if aString is
					//   neither NULL nor points to a
					//   malloc'ed chunk.
	    [aSomething free];		// If aSomething is nil, any
					//   message sent to it is a no-op.
	    return [super free];	// The convention is for all -free
					//   methods to return nil.
	}

	@end

> Is it worth contacting NeXT?

Definitely about the manual, and probably about having run-time errors
generated in the situation you presented.  Are you going to?

Jacob
--
Jacob Gore		Jacob@Gore.Com			boulder!gore!jacob

eps@toaster.SFSU.EDU (Eric P. Scott) (01/13/90)

In article <130056@gore.com> jacob@gore.com (Jacob Gore) writes:
>This is indeed a bad example.  It's technically correct, but it belongs in
>something like "Objective-C Traps and Pitfalls", not as an example for the
>typical use of -free.

ok, but ...

>Besides, 'self' is assignable.

Bingo.  (This may be more useful in class methods than instance methods.)

Note: If you're using the Application Kit, Application's
delayedFree: may be just what you're looking for.

					-=EPS=-

dennisg@kgw2.uucp.WEC.COM (Dennis Glatting) (01/13/90)

In article <1842@opus.cs.mcgill.ca>, clement@opus.cs.mcgill.ca (Clement Pellerin) writes:
> 
> The example is [self free].  Note that self will be deallocated by
> the time this message returns.  The object class of self will not be deallocated

hmm. remember that self is just a pointer to a memory location.  just
because one malloc() something doesn't imply it can't be accessed after free().
the results cannot be predictable.

therefore, yes, i beleive you can access those instance variables.  self,
a simple pointer, isn't erased.  uou simply forget about it.

i would think that if you subclass had a free method then you might get
a infinite loop with [ obj free ].  don't ya tink?

> Is it worth contacting NeXT?
> 

sure, just email bug_next@next.com.

--
 dennisg@kgw2.bwi.WEC.COM  | Dennis P. Glatting
 ..!uunet!tron!kgw2!dennisg |
   <<This section isn't available on-line.>>

ed@DTG.COM (Edward Jung) (01/14/90)

In article <1842@opus.cs.mcgill.ca>, clement@opus (Clement Pellerin)
writes:

[a bunch of stuff]


This is a rather long message in reply to Clement Pellerin's question
about [self free] in Objective-C.

SOME BACKGROUND

Pure object oriented languages do not allow direct access to instance
variables; all access is mediated by messages.  In Smalltalk and
CLOS (Common Lisp Object System), for example, you do not access
instance variables directly, but rather through an access method.
Objective-C, being a hybrid language anyhow, allows instances to
access their own instance variables directly, and the instance
variables of other instances that have been declared using the
@public keyword or via structs and the @defs keyword.

One side-effect of direct access to instances is that instance
variables must be fixed in size throughout the duration of an
execution of a program.  You can add methods to a class definition
at run-time, but you cannot change the size or number of a class'
instance variables.  (We are working on a system that will allow
this via incremental run-time relink).


HOW THE COMPILER MAKES AN OBJECT

An object (class instance) is allocated using the equivalent of
malloc().  This is performed by Object's new method.  The symmetric
free() is performed by Object's free method.  Since all allocation
and freeing is performed via the inheritance chain (calls to super),
all objects end up allocated and freed in this manner.

Classes are allocated at load time, along with information about
the methods, method names, argument types, selectors, instance
variables, instance variable names, and instance variable types.
All this information is available at run-time and is embedded in
the OBJC segment in a Mach-O file.  Note also that there is a
provision for class variables, but the Objective-C syntax does not
handle this (yet?).


HOW THE COMPILER HANDLES INSTANCE VARIABLE ACCESS

Every reference to an object's instance variables is translated to
an offset pointer lookup to self.  self is a parameter that is
secretly passed into every method (as is the selector for that
method); it is used implicitly to reference an object's instance
variables.  Assume the following method in the class MyObject, and
anInstanceVariable is an instance variable of MyObject:

    - setAnInstanceVariable:(int)toThisInt
    {
	anInstanceVariable = toThisInt;
	return self;
    }

The code produced by the compiler actually looks something like
this (in ANSI C syntax):

    id setAnInstanceVariable(id self, SEL _cmd, int toThisInt)
    {
	self->anInstanceVariable = toThisInt;
	return self;
    }

So methods are converted into ordinary functions.  Of course the
real code is output in object code on the NeXT, and the function
name is bound to a rather strange name, but the basic idea is the
same.


HOW THE COMPILER HANDLES MESSAGE SENDS

Message sends, on the other hand, are all vectored through one of
two message dispatchers: objc_msgSend() and objc_msgSendSuper()
(or equivalent).  These dispatchers are given the value of the
receiver of a message, and the message selector, perform a lookup
to find the function (the converted method, as in the above example)
associated with the receiver and selector, and jump to that function.
In this process, the dispatcher examines the "isa" link of the
receiver.  If it has an invalid value, the dispatcher gives a
run-time error.  WHEN YOU FREE AN OBJECT USING [anObject free],
THE isa LINK IS GIVEN AN INVALID VALUE, so the error is generated.
This is to assist in trapping the occasional bug where you send a
message to a freed object.

Since access to instance variables is performed through a pointer
offset, this mechanism is not sensitive to the value of the isa
link.  As this is a very fast means of accessing the instance data,
the overhead of adding a check of the isa link to every such call
would be significant (since method lookup is a rather more complex
process, the lookup is relatively less expensive).


GETTING BACK TO THE QUESTION

So the example given by NeXT is correct, though perhaps ambiguous.
[self free] is the correct way to release the memory associated
with an object.  [self free] is semantically equivalent to:
    
    char * aPtr;
    ...
    aPtr = malloc(256);
    ...
    free(aPtr);
    /* aPtr is now freed */

In the above example, you could access aPtr after freeing it, but
that's not the "right" thing to do.

You can still exist in the scope of the method that called [self
free], because freeing an object does not do anything to the method
code; it does, however, invalidate certain variables (again, just
like the malloc/free example above).

This is why the safest way to write a free method in a class is as
follows:

    - free
    {
	free(aPtr);
	/* free all the other junk */
	return [super free];
    }


WHAT IS WRONG WITH THE SUICIDE EXAMPLE?

You should not do anything with an object after it has been freed.
Just like a dynamically allocated pointer.

    Should an object that does not exist anymore be allowed to
    continue executing a method if it does not access its variables
    nor sends a message to itself?

Yes.  Its storage has been freed, but the methods are independent,
and might be thought of as owned by the class, which still exists.
The definition of an instance of a class is essentially a copy
of the data formed from the template defined in the class; the
methods are shared by all the instances, and thus are independent
from the existance of the instances.

    I would prefer if I would get a run-time error by accessing
    the variables.

This is a problem stemming from the semantics of dynamically
allocated memory without garbage collection.

    Is it worth contacting NeXT?

I don't think so.  Perhaps the manual could be made clearer, but
the semantics of Objective-C are correct for its model.  Perhaps
the thing to wish/ask for is garbage collection, if memory management
is a headache.  In C, dynamically allocated objects (instances,
pointers or otherwise) are the programmers' responsibility to track.

Note that you can do some interesting things with the new and free
semantics, such as implementing an object cache to minimize heap
fragmentation from repeated allocations of object instances and/or
the instance variables that are dynamically allocated pointers:

    #define FOP_SIZE (32)
    static MyClass free_object_pool[FOP_SIZE];
    static int num_in_fop = 0;
    
    + new
    {
	if (num_in_fop > 0) {
	    num_in_fop--;
	    self = free_object_pool[num_in_fop];
	    anInt = 100;
	}
	else {
	    self = [super new];
	    anInt = 100;
	    aPtr = malloc(10000);
	}
	return self;
    }
    
    - free
    {
	if (num_in_fop < FOP_SIZE) {
	    free_object_pool[num_in_fop] = self;
	    num_in_fop++;
	    return nil;
	}
	else {
	    free(aPtr);
	    return [super free];
	}
    }

Of course in this example you may be able to continue to send
messages to instances that have been freed, but you should never
do that anyhow.

There are a host of other optimizations that can be done with
Objective-C, including determining the static address of a method
to avoid message passing overhead in inner loops, etc.

Further detail about this and other Objective-C matters might be 
better addressed via email to conserve news bandwidth.


-- 
Edward Jung                             The Deep Thought Group, L.P.
BIX: ejung                                      3400 Swede Hill Road
NeXT or UNIX mail                                Clinton, WA.  98236
        UUCP: uunet!dtgcube!ed          Internet: ed@dtg.com