This plugin does work on the carbon os-x VM (3.18.x) which makes testing easier, it is available at: ftp://ftp.smalltalkconsulting.com/experimental/ , however much of the iPhone specific features are not available under OS-X. (The plugin is built as internal to the iPhone VM, so no external plugin is needed)
The goal of this plugin was to have it coded entirely in Slang and minimize the amount of code that is actually in the plugin. In general
only a handful of people actually build plugins, so the process of updating that code for fixes or enhancements is slow. If the majority
of the code is in Smalltalk then it's easier for anyone to submit a fix or enhancement to the MC server.
On the Smalltalk side a decision was made to use ProtoObject as the super class so we could use Does Not Understand to redirect method invocation to the Objective-C objects. This was a bit troublesome because it required bringing down a large number of methods from Object to make an objective-c class/subclass to work. These methods include:
('printing' longPrintOn:limitedTo:indent: longPrintStringLimitedTo: printOn: printString printStringLimitedTo:)
('smalltalk methods' asExplorerString defaultLabelForInspector error: halt inspect inspectorClass instVarAt: isKindOf: isMorph isNumber isString primitiveFailed smalltalkClass)
The ObjectiveCObject class contains one instance variable externalAddress which is the address of the Objective-C object.
We note at shutdown time if quitting all instances and subinstances of ObjectiveCObject have their externalAddress set to zero,
because they are only valid for the currently running image, not across image restarts.
If the address is zero and an attempt is made to use it as a valid Objective C address then a walkback will occur.
A small number of accessors are available:
descriptionAsString - Invoke description against the Objective-C object and return the result as a smalltalk string
externalAddress - access the address with error check for zero
externalAddressPrivate - PRIVATE access the address with no error check, do not use this
externalAddress: - Set the address of the Objective-C object
objectiveCClass - Return the class of the object, we note asking the object for class will give you the smalltalk class.
objectiveCHash - Return the Objective-C hash
objectiveCSuperClass - Return the Objective-C super class
= - Return false if not the same class, otherwise return true if the same address, or invoke isEqual:to:
hash - Return the objective-C hash, if the object is nil then return hash of the ObjectiveC NSNull singleton
Other methods implemented were:
('testing' ifNil: ifNil:ifNotNil: ifNotNil: ifNotNil:ifNil: isNil isObjectiveCObject)
Which check to see if the object is NULL.
NULL here means is the external Address zero, meaning C language NULL, or is the Objective-C object NSNull?
isObjectiveCObject - is true for class/subclasses of ObjectiveCObject, false otherwise, it's used by the DNU logic.
Class Side methods are:
findClassName: - use to find the class object for the given class name (a smalltalk string)
newWithExternalAddress: - used by the internal logic to build a new instance from an objective-C address
Because there is a limited number of class side and instance side methods likely the Objective-C method you want to
invoke does not exist as a smalltalk method. This means Does Not UnderStand will be triggered, this then uses the
doesNotUnderstand: method to forward the request to the ObjectiveCBridge class for resolution.
The plugin includes an Objective C class squeakSUnitTester to enable testing.
Say we want to create an instance of the class, then invoke the test1unsignedchar: method which returns the
supplied unsigned character we pass in as a value.
classOop := ObjectiveCObject findClassName: 'squeakSUnitTester'.char := instance test1unsignedchar: $B.
instance := classOop new.
instance := classOop new.
self should: [char = $B].
As you see once we resolve the classOop we just send messages to the instance like we would do with a normal Smalltalk object.
(Keep in mind the few specialized methods for class and superclass)
Note we could have done
instance := classOop alloc init.We note in these examples the Smalltalk parser may complain that some method you are trying to use does not exist in the image, this of course is true, just confirm your choice and continue. The parser is warning you invoking this method will result in a DNU, but we *are* wanting that...
The reason for subclassing ObjectiveCObject is to provide accesssor methods that avoid most of the DNU logic. We have one example of this the ObjectiveCNSMethodSignature class which is frequently used to resolve the method signatures to invoke in the DNU logic. Thus we code calls to
methodNumberOfArguments methodReturnLength methodReturnType to invoke the required primitives otherwise the DNU logic would run into a recursive call situation.
This class is a subclass of Object because it is only a factory that returns the sole instance of NSNull.
This class is a subclass of Object, a CSEL object is actually a pointer to C string it's not a Objective-C object. However it mimics one since
it is passed around in the DNU logic.
This object (on the class side) contains the logic for forwarding message sends to objective C objects, all the plugin primitive interface, and logic to cache Objective-C Class and Selector instances.
When a DNU occurs this is sent to forward:to: which attempts to determine if the message send has zero, one, two or more parms. For the case where it has zero, one or two parms and returns an object and fits a particular pattern for the parms the perform selector on the object with parms is invoked. This is the fast simple case.
For more complex message sends then we must go thru the complex task of making a NSInvocation instance and allocating storage for the return value if needbe, then iterating over each parm to allocate storage and convert the given object to the expected form then issuing invoke and retrieving the expected value.
In general you should NOT use this methods on this class directly in your code. An exception would be if you choose to subclass ObjectiveCObject and create methods mirroring Objective-C methods for performance reasons. You should test to confirm that you DO get a performance benefit before doing this since the ObjectiveCBridge methods are mostly PRIVATE. However under special circumstances one could do the follow if you had a subclass ObjectiveCObjectSubClass mapping to an Objective-C class ObjectiveCObjectSubClass.
instanceOop1 := ObjectiveCBridge performSelectorNamed: 'new' onClassNamed: ObjectiveCObjectSubClass name noReturn: false useClass: ObjectiveCObjectSubClass.Which *might* be faster than doing