GNU Smalltalk 1.2 alpha 3 <-> NeXT Objective-c Interface Explanations, Guidelines, and Hints

A Crappy Explanation Of How The Interface Works.  If you read this and it makes no sense to you please email me with suggestions.  Its kind of hard to explain this crap.

Table of 

pure smalltalk object
pure objective-c object

Abstract

Creating the Classes

What this interface does is create a new smalltalk classes for each class in the appkit by quering the objective-c runtime system using a few functions written by me or snagged from other code and adopted for this evil purpose.  Starting with objective-c's Object and mapping it to smalltalk's OCObject the entire objective-c class heirarchy is duplicated (well not all, I wrote some smalltalk code that ignores most of the undocumented appkit classes). Each class has a single class variable to the objectice-c class and an inherited instance var which points to the id of the objective-c objects, once it has been instantiated.

See the file OCInterface.st and cfunc.c to see the C and smalltalk code used to create the interface.

Creating the Methods

The methods are created by querying the objective-c runtime system with some special functions and then
creating smalltalk code to invoke the objective-c method.  The actual methods are sent using the function objc_msgSend().  For each new method a class needs to implement it creates a special call to the function objc_msgSend using the following smalltalk code

	Behavior defineCFunc: 'msgSend'
		withSelectorArgs: 'cObjectMsgSend1: anId sel: aSelector argSmalltalk: arg0'
		forClass: OCInterface class
		returning: #cObject
		args: #(cObject cObject smalltalk).

The value 'msgSend' in the first line tells smalltalk to associate the next line with the C function objc_msgSend().  The next line is created by some C code that looks at the objective-c selector to see what its return type is and args are and then creates a unique string to represent this smalltalk callout to C code.
The forClass: field tells smalltalk to add this method as a class method to OCInterface.  BTW OCInterface is a instances less class used to create the smalltalk<->objective-c interface.  The last two lines tell smalltalk host to convert the args passed to  cObjectMsgSend1 to C types.  The cObject type is a pointer and is used to pass objective-c's id type around.

But you you're saying I don't want every method to go through OCInterface and have these horrid names.  But wait it gets better.  The new Class method to OCInterface is then used by a automatically created class like so  

	setOOP: arg0 
		| anId aSelector rValue | 
		anId _ id.
		aSelector _ OCInterface getUid: 'setOOP:'.
		rValue _ OCInterface cObjectMsgSend1: anId sel: aSelector argSmalltalk: arg0.
		((OCInterface id: id equalTo: rValue) = 1)
			ifTrue: [ ^self ]
			ifFalse: [ ^OCObject newFromId: rValue. ].

The code from above is for the smalltalk version of the objective-c class STObject.  This code is (usually) automatically generated for each method.  Notice that is checks to see what the return type is, if its a cObject (maybe an id) we check to see if it is equal to our id, if it is we return our smalltalk self.  If its not equal to our id then it wraps the object in another smalltalk object using OCObject's class method newFromId:.  If the value returned is a pointer to a structure or something along those lines, things will die.  Luckily appkit functions almost never return any pointers via return but by reference.  Passing stuff by reference will work but you need to get a handle to the pointer first.  We'll discuss how that works later though.

To forward an objective-c message to smalltalk works a little different.  In order for an objective-c object to forward a method to a smalltalk object it must know it exists and it also needs a way to tell the code that creates the interface between the two languages that certain objective-c methods aren't to be wrapped by smalltalk.  For this to work a new objective-c class was created called STObject.  It has an OOP type (the type OOP is how smalltalk objects are represented in the C smalltalk interpreter) ivar that has to be set  explicitly or things will not work.   The OOP ivar, named stObejct, is set using the setOOP: method and can be called from smalltalk code as

		self setOOP: self.
		
This work because the self setOOP part sends the setOOP: message to the id and passes self (Internally represented as an OOP in the smalltalk interpreter) as an OOP to the  objective-c method.

If this is confusing dont' worry I don't think you'll need to understand it to be able to use it.

Problems, Make Sure You Aren't Doing the Following

1.	Avoid doing this
		browser setDelegate: aDelegate.
	because your telling an objective-c object (NXBrowser) that its delegate is
	a smalltalk object.  What you meant was
		browser setDelegate: aDelegate id.
	The #id method returns the actual id value of a smalltalk/objective-c object.
	
2.	If you're subclass STObject of one of the STDelegate subclasses make sure you call
	super's init method since it is this method that properly sets up the 2-way communication.