Here's a rough sketch of the implementation. - NSInvocation is used to invoke methods, based on a design suggestion from the list, and the design of the Ruby and Gauche bridges. Advantages: does not use libffi, parses arguments and return types for you. Disadvantages: NSInvocation has some annoying behavior and also a critical flaw. - The critical flaw is that NSInvocation cannot send to target 'super'. super of course is not the superclass of 'self', but the superclass of the class where the executing method is defined. To hack around this, when a method is defined I search for any overridden supermethods and define a new selector with prefix 'classname:super:...' pointing to the supermethod. Executing methods are then provided with their classname via a closure, and a macro transform can convert a @[super ...] call to the appropriate selector. Using objc_msgSendSuper would obviate this hackery, and it dynamically considers the class structure. (However, the above solution does give us exact supermethod signatures, which it seems cannot reliably be obtained at runtime. This is used for correct type conversion.) - FFI is required for method proxies. Originally, I passed a function with an IMP signature-- (id (*)(id self, SEL _cmd, ...)) -- and used va_args to obtain its arguments. This was unreliable. Double and float values were not passed correctly to va_args, and trying to parse the stack directly was not possible. Return types were also problematic; returning a double is impossible through a 4-byte return type, and doing the reverse is a crapshoot. I was forced to use libffi, but it works perfectly. Also, the FFI closures are useful. - The Scheme procedure is passed thru the FFI closure. - Method return and argument types are passed thru the closure (as strings). I tried to use NSMethodSignature, but methodSignatureForSelector: did not reliably find our signature, but sometimes that of a supermethod. - All Objective C invocations are marked as safe: because any one could potentially call back into a Scheme class at some arbitrary point. I don't like this, but don't understand enough to implement it better. - Type conversion is done is Scheme, with many conversions ultimately relying on the type conversion performed by foreign-lambda calls. This is easier to understand than my first attempt in pure C, but probably slow. In the end, now that I have dragged in a bunch of pure C functions for the FFI type handling, I've cluttered up the source anyway. - Overall it is just slow. Not much optimization has been done. - I like the way Felix had simple calls go through a foreign-lambda instead of the lazy-ffi layer. Before restoring this functionality I need to make sure memory management will still work correctly with selectors such as alloc. - Memory management is a topic unto itself and I need to collect my thoughts on it. I am not sure if the current approach will last, as I have nagging feelings about certain cases I have not tested. ;; Return values which are newly allocated IDs (auto-converted strings or objects that ;; were alloced in the body of a function) may be destroyed early because the garbage collector ;; loses its hold on them when the instance is passed through ptr->ref. The same is true ;; for auto-converted string arguments and other ephemeral ID arguments. ;; So, we deliberately retain and autorelease any object about to pass through the ptr->ref barrier. ;; This has the following effects: ;; 1. When calling from Scheme ;; a. Newly allocated proxy return values have their lives extended until the end of the ;; NSInvocation. They are alloced into an objc:instance (retain: 1), retained+autoreleased ;; (retain: 2), converted ptr->ref, finalized (the objc:instance) (retain: 1), wrapped in a new ;; objc:instance in objc:invoker (retain: 2) and then autoreleased there (retain: 1). ;; b. NSString arguments created here also live until the end of the NSInvocation. ;; c. ID arguments passed directly to @[...] are guaranteed to stay alive during the ;; NSInvocation; otherwise, the GC could reclaim them. ;; d. Background: Allocating selectors (such as "alloc") are assumed to donate a reference. ;; An object is returned (retain: 1), converted to an objc:instance (retain: 2) and ;; released (retain: 1) to removed the donated reference. It is later finalized (retain: 0). ;; ;; Therefore: Simply returning @[super alloc] in a Scheme alloc proxy results in a ;; reference count one too low. It would be alloced (retain: 1), retained+autoreleased (2), ;; finalized (retain: 1), wrapped in a new objc:instance (retain: 2), adjusted for ;; donated reference (1), autoreleased (0). ;; ;; Instead, @[@[super alloc] retain] should work. Odd, but effective. ;; ;; Or at least, that's how it -should- work. But @[super alloc] seems to work fine ;; even in an allocating selector. This needs to be investigated. ;; ;; 2. When calling from ObjC ;; a. Newly allocated return values (retain: 1) will be retained+autoreleased (retain: 2), ;; finalized (retain: 1) and autoreleased (retain: 0) when the caller's autorelease pool ;; expires.