Jonathan Paisley
jp-ww****@dcs*****
Sat Feb 11 20:17:15 JST 2006
Hi, I've been playing with a patch that ensures that RBObject instances created for Ruby objects are unique, rather than creating a new RBObject each time a Ruby object is passed through the bridge. I had initially hoped this might make it easier to use an NSOutlineView with non-NSObject-derived Ruby objects, but because NSOutlineView doesn't retain the items this doesn't really work. In any case, I think it's nice to have a 1-1 mapping between Ruby objects and RBObject. Perhaps it might be worth doing the same for ObjCID instances too (using another hash table to map from 'id' to VALUE). I've included the current patch below. If you think it's worth pursuing further, I'll add test cases etc. If not, I'll keep the patch around in case it becomes useful in the future. Cheers, Jonathan === src/objc/RBObject.m ================================================================== --- src/objc/RBObject.m (revision 32) +++ src/objc/RBObject.m (local) @@ -16,6 +16,8 @@ #import "ocdata_conv.h" #import "DummyProtocolHandler.h" +#import "st.h" + #if 1 # define DLOG0(f) if (ruby_debug == Qtrue) debug_log((f)) # define DLOG1(f,a1) if (ruby_debug == Qtrue) debug_log((f),(a1)) @@ -28,6 +30,12 @@ # define DLOG3(f,a1,a2,a3) debug_log((f),(a1),(a2),(a3)) #endif +// Hash table mapping VALUE to id, so it's possible +// to get the same RBObject for each VALUE that +// isn't a number, string, hash, array etc (since +// these are dealt with specially elsewhere). +static struct st_table *rbobject_map; + static void debug_log(const char* fmt,...) { // if (ruby_debug == Qtrue) { @@ -301,15 +309,39 @@ DLOG1(" --> rb_result=%s", STR2CSTR(rb_inspect(rb_result))); } + +// internal initialiser (doesn't lookup hash) +- (id) initWithRubyObjectInternal: (VALUE)rbobj +{ + m_rbobj = rbobj; + rb_gc_register_address (&m_rbobj); + st_insert(rbobject_map, (st_data_t) rbobj, (st_data_t) self); + return self; +} + ++ (void) load +{ + rbobject_map = st_init_numtable(); +} + // public class methods ++ RBObjectWithRubyObject: (VALUE)rbobj +{ + st_data_t result; + if (st_lookup(rbobject_map, rbobj, &result)) { + return (RBObject*)result; + } + return [[[RBObject alloc] initWithRubyObjectInternal: rbobj] autorelease]; +} + + RBObjectWithRubyScriptCString: (const char*) cstr { - return [[self alloc] initWithRubyScriptCString: cstr]; + return [self RBObjectWithRubyObject: rb_eval_string(cstr)]; } + RBObjectWithRubyScriptString: (NSString*) str { - return [[self alloc] initWithRubyScriptString: str]; + return [self RBObjectWithRubyScriptCString: [str UTF8String]]; } // public methods @@ -318,15 +350,23 @@ - (void) dealloc { - rb_gc_unregister_address (&m_rbobj); + if (m_rbobj) { // check in case we get released in initWithRubyObject: + rb_gc_unregister_address (&m_rbobj); + st_data_t key = (st_data_t) m_rbobj; + st_delete(rbobject_map, &key, NULL); + } [super dealloc]; } - initWithRubyObject: (VALUE)rbobj { - m_rbobj = rbobj; - rb_gc_register_address (&m_rbobj); - return self; + st_data_t result; + if (st_lookup(rbobject_map, rbobj, &result)) { + // Release self, return some other object + [self release]; + return [(RBObject*)result retain]; + } + return [self initWithRubyObjectInternal: rbobj]; } - initWithRubyScriptCString: (const char*) cstr === src/objc/ocdata_conv.m ================================================================== --- src/objc/ocdata_conv.m (revision 32) +++ src/objc/ocdata_conv.m (local) @@ -389,7 +389,7 @@ case T_FALSE: case RB_T_DATA: default: - *nsobj = [[[RBObject alloc] initWithRubyObject: obj] autorelease]; + *nsobj = [RBObject RBObjectWithRubyObject: obj]; return YES; } return YES;