3. Bridge Notes
3.1 Calling conventions: calling Objective-C from Ruby
RubyObjC uses a simple convention to call Objective-C methods from Ruby. Colons (:) in selector names are replaced with underscores (_), and all parts of the selector are concatenated to form the Ruby method name.
For example, to call the makeObjectsPerformSelector:withObject: method of NSArray, you would write Ruby code that looked like this:
myArray.makeObjectsPerformSelector_withObject_("danceWith:", me)
Here we assume that myArray is an NSArray containing objects that are able to respond to the “danceWith:” selector, and that me is an acceptable argument value to send with the selector.
3.2 Calling conventions: calling Ruby from Objective-C
When they are properly declared, Ruby methods are easy to call from Objective-C. In fact, the Objective-C code probably won’t be any different from code that callse Objective-C methods. To the Objective-C caller, RubyObjC methods look exactly like methods written in Objective-C.
For a Ruby method to be available from Objective-C, it must first be a method of a class in the hierarchy rooted at ObjC::Object. Usually this means that its Ruby class is derived from ObjC::NSObject or the wrapper for a Cocoa class derived from NSObject, such as ObjC::NSWindowController.
Ruby methods can be added to Objective-C classes either as instance methods or as class methods.
Adding Instance Methods to Objective-C classes from Ruby
To add a Ruby method as an instance method, declare it with the imethod declarator. For example, the following adds a simple instance method to an application delegate class:
class ApplicationDelegate < ObjC::NSObject imethod "applicationDidFinishLaunching:", "v@:@" do |sender| ... end end
In this case, the applicationDidFinishLaunching: method is not known to the Objective-C runtime, but because it resembles an action, its signature will be assumed to be “v@:@” and could be safely omitted. There’s more on this below.
Adding Class Methods to Objective-C classes from Ruby
To add a Ruby method as a class method, declare it with the cmethod declarator. For example, the following adds an initialize method to an application delegate class:
class ApplicationDelegate < ObjC::NSObject cmethod "initialize", "v@:" do .... end end
In this case, the “initialize” method is known to the Objective-C runtime and its signature could be safely omitted.
In general, when a method signature is not specified in an imethod or cmethod declaration, RubyObjC will look in the Objective-C runtime for a method with the same name as the method being declared. If one exists, its signature is used. If no such method exists, the bridge has two fallbacks:
1. If the method name looks like an action (a single word ending in ’:’ ), then it is given a default signature of v@:@.
2. If the method name looks like an accessor (a single word containing no ’:’), then it is given a default signature of @@:.
To get the signature to use with a method, concatenate the type codes for its return type and arguments. The return type is first, then there are codes for two hidden arguments, the message receiver ’@’ and the selector being sent ’:’. That’s followed by the codes for each argument.
You can refer to Apple’s Developer Connection web site for a description of the individual type codes.
You can also use RubyObjC to check the signature of any method in the Objective-C runtime. For details, see the next section.
Looking deeper
To easily see whether a method is known to the Objective-C runtime, use the ObjC.get_methods function to get a Ruby hash containing all known methods, indexed by the method name. For exampleirb(main):001:0> m = ObjC.get_methods ... irb(main):002:0> m["initialize"] => initialize irb(main):003:0> m["initialize"].signature => "v@:"Be careful, though, sometimes multiple methods exist with the same name and different signatures. When this is the case, the get_methods function currently returns only one such method, which is the one that would be used to automatically determine the signature of methods added without explicitly-specified signatures. When there’s doubt, it’s best to explicitly specify a signature.
RubyObjC also provides ways for you to get to the methods associated with specific classes. Here’s one example:
irb(main):007:0> ObjC::NSImageView.imethods.select {|m|
irb(main):008:1* m.name == "imageAlignment"}[0].signature
=> "i@:"
For more details, see the online documentation for
ObjC::Object,
ObjC::Class, and
ObjC::Method.
3.3 Working with Objective-C objects from Ruby
irb(main):001:0> ObjC::NSObject.new
NoMethodError: undefined method `new' for ObjC::NSObject:Class
from (irb):2
Did you find an error? Is something missing? Post your comment or suggestion below!
Comments (4) post
Aw, this was a really quality post. In theory I’d like to write like this too – taking time and real effort to make a good article… but what can I say… I procrastinate alot and never seem to get something done.
Great post. Thanks for an interesting read. I’ve just subscribed to this blog so I’ll be back soon! Cheers
There will be efforts at communication abroad, canvassing and organization of international commercial missions. This should all begin in the coming months.
Aw, this was a really quality post. In theory I’d like to write like this too – taking time and real effort to make a good article… but what can I say… I procrastinate alot and never seem to get something done.