Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
MacRuby with Objective-C associated objects
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <MacRuby/MacRuby.h>
@interface NSObject (MRAssociatedObjects)
- (id)associatedObjectForSymbol:(NSString *)rubySymbolName;
- (void)setAssociatedObject:(id)value forSymbol:(NSString *)rubySymbolName;
@implementation NSObject (MRAssociatedObjects)
static VALUE
symbolNameToSymbol(id rubySymbolName)
VALUE sym;
ID id_to_s = rb_intern("to_s");
if (SYMBOL_P(rubySymbolName)) {
sym = (VALUE)rubySymbolName;
} else if ([rubySymbolName isKindOfClass:[NSString class]]) {
sym = rb_name2sym([rubySymbolName UTF8String]);
} else {
if (rb_respond_to((VALUE)rubySymbolName, id_to_s)) {
VALUE str = rb_funcall((VALUE)rubySymbolName, id_to_s, 0, Qnil);
sym = rb_name2sym(RSTRING_PTR(str));
else {
[NSException raise:@"ArgumentError"
format:@"Expected a Symbol or NSString, but got `%@'", rubySymbolName];
return sym;
- (id)associatedObjectForSymbol:(id)rubySymbolName
return objc_getAssociatedObject(self, (void *)symbolNameToSymbol(rubySymbolName));
- (void)setAssociatedObject:(id)value forSymbol:(id)rubySymbolName
objc_setAssociatedObject(self, (void *)symbolNameToSymbol(rubySymbolName), value, OBJC_ASSOCIATION_RETAIN);
// Ruby expects this
Init_associated_objects() {}
@interface TestAssociatedObject : NSObject
@implementation TestAssociatedObject
- (void)computeAnswerInObjC
if ([[self associatedObjectForSymbol:@"question"] isEqual:@"the answer to everything"]) {
[self setAssociatedObject:[NSNumber numberWithInt:42] forSymbol:@"computed_answer"];
% clang -fobjc-gc -framework Foundation -framework MacRuby -bundle -o associated_objects.bundle associated_objects.m
% macruby test.rb
/Users/eloy/tmp/associated-objects/test.rb:6:in `[]=:': ArgumentError: Expected a Symbol or NSString, but got `42' (RuntimeError)
from /Users/eloy/tmp/associated-objects/test.rb:13:in `<main>'
require "associated_objects"
class NSObject
alias_method :[], :associatedObjectForSymbol
def []=(symbol, value)
setAssociatedObject(value, forSymbol:symbol)
o =
o[:question] = "the answer to everything"
p o[:computed_answer] # => 42
# anything other than a string/symbol makes it go boom
o["string is ok too"] = "yup"
o[42] = "ohnoes" # => test.rb:6:in `[]=:': ArgumentError: Expected a Symbol or NSString, but got `42' (RuntimeError)
p o[42]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment