[MacRuby-devel] Using performRubySelector

Mike Laurence mklaurence at gmail.com
Wed May 27 23:25:51 PDT 2009


Hmmm... I'm doing several things different, perhaps one or more of
them are impossible :-)

Basically, I'm trying to get MIDI support into Ruby via the PYMIDI
obj-c library, which is really just a wrapper around CoreMIDI. One way
I had thought of: create an additional obj-c class (MidiReceiver)
which processes incoming packets for a given MIDI source and then call
methods such as "note", "controlChange", "clockTick", etc. This
MidiReceiver class can then be overridden by a custom ruby class that
contains those same methods.

Here is a quick version of the MidiReceiver class (with only the
clockTick method for simplicity):

------------- MidiReceiver --------------

#import <MacRuby/MacRuby.h>
#include <CoreMIDI/CoreMIDI.h>

@interface MidiReceiver : NSObject

- (void) processMIDIPacket: (MIDIPacket*) packet;
- (void) clockTick;

@end

@implementation MidiReceiver

- (void) processMIDIPacket: (MIDIPacket*) packet {
    if (packet->length > 0) {

        int statusByte = packet->data[0];
        int status = statusByte >= 0xf0 ? statusByte : statusByte >> 4 << 4;

        switch (status) {
        case 0x90: // Note on, etc...
        case 0xf8: // Clock tick
            [self performRubySelector:@selector(clockTick)];
            break;
        }
    }
}

- (void) clockTick {
}

@end

--------------------------------------------------------

Then, I have a ruby subclass of MidiReceiver that overrides clockTick, etc.:

class LiveMidiReceiver < MidiReceiver
  def clockTick
    puts "tick!"
  end
end

And then, in my Ruby ApplicationController, I'm finding the MIDI
source and adding an instance of LiveMidiReceiver as a MIDI receiver:

@src = PYMIDIManager.sharedInstance.realSources.find{ |s|
s.displayName == 'KONTROL49 PORT A' }
receiver = LiveMidiReceiver.new
@src.addReceiver(receiver)

The LiveMidiReceiver instance, upon receiving a midi packet, is called
properly up to the point of the performRubySelector, but thereafter it
launches into the debugger with EXC_BAD_ACCESS messages or other
unsightly stack dumps.

---

An even *better* interface would be to have the "clockTick" and other
calls be performable on an arbitrary ruby object without having to
subclass MidiReceiver (e.g., have MidiReceiver send "clockTick", etc.
to a delegate object which has been created solely in Ruby). I tried
that and it gave me similar results, although strangely it only
crashed the first time on a clean build - thereafter I saw no crashes,
but still no confirmation of ruby method calls either.

To test that, I just added a delegate object to MidiReceiver, and then
I changed the clockTick recipient from self to delegate:

[delegate performRubySelector:@selector(clockTick)];

Then set receiver.delegate = self in my ApplicationController. I'll
bet I need some more hooks than that, although it sure would be nice
to send ruby messages from obj-c willy-nilly :-)

---

I hope I'm making some sense here! I greatly appreciate any info that
you can send my way. Hopefully when I get this all figured out I can
write a nice, fat blog post about it :-)

Regards,
Mike Laurence

On Wed, May 27, 2009 at 8:18 PM, Laurent Sansonetti
<lsansonetti at apple.com> wrote:
> Hi Mike,
>
> On May 26, 2009, at 5:45 PM, Mike Laurence wrote:
>
>> Hello,
>>
>> I'm trying to get some obj-c code to talk back to my ruby. After
>> encountering some "EXC_BAD_ACCESS" messages and scouring the web, I've
>> concluded that I'm probably supposed to use performRubySelector
>> instead of just expecting selectors to work when overridden by ruby
>> subclasses, etc.
>>
>> What is the preferred way to get this to work? I looked through some
>> of Elysium's old code (which used performRubySelector), but I'm having
>> trouble wrapping my head around how you're supposed to use the MacRuby
>> sharedRuntime to get things to happen. If someone could give me a
>> quick example of how to call arbitrary ruby methods, I would highly
>> appreciate it.
>>
>> Of course, if I'm completely off base and there's some other way to
>> call ruby code, please let me know!
>
> Calling Ruby from Objective-C can be problematic, depending if you want to
> call a pure Ruby method or a Ruby method that overrides an Objective-C one.
> If you want to dispatch the method by yourself (and if it's a Ruby method
> that overrides a specialized Objective-C method), you may want to be very
> careful about the types of the arguments you are passing to, as well as the
> return value.
>
> In any case, using performRubySelector: is better because arguments will be
> converted from Objective-C objects (id) into the expected type, and the
> return value will be converted into an Objective-C object. Also,
> performRubySelector: can deal with Ruby methods that have optional or splat
> arguments.
>
> $ cat t.m
> #import <Foundation/Foundation.h>
> #import <MacRuby/MacRuby.h>
>
> @interface Foo : NSObject
> @end
>
> @implementation Foo
> - (int)aMethodReturningInt
> {
>    return 123;
> }
> @end
>
> int main(void)
> {
>    [[MacRuby sharedRuntime] evaluateString:@"class X; def foo(x=1, *a); p x,
> a; end; end"];
>    Class k = NSClassFromString(@"X");
>    id o = [k new];
>
>    [o performRubySelector:@selector(foo)];
>    [o performRubySelector:@selector(foo:) withArguments:@"1", NULL];
>    [o performRubySelector:@selector(foo:) withArguments:@"1", @"2", @"3",
> NULL];
>
>    [[MacRuby sharedRuntime] evaluateString:@"class Bar < Foo; def
> aMethodReturningInt; 42; end; end"];
>    k = NSClassFromString(@"Bar");
>    o = [k new];
>
>    NSLog(@"%d", [(Foo *)o aMethodReturningInt]);
>    NSLog(@"%@", [o performRubySelector:@selector(aMethodReturningInt)]);
>
>    return 0;
> }
> $ gcc t.m -o t -framework Foundation -framework MacRuby -fobjc-gc
> $ ./t
> 1
> []
> "1"
> []
> "1"
> ["2", "3"]
> 2009-05-27 18:16:36.092 t[11922:10b] 42
> 2009-05-27 18:16:36.095 t[11922:10b] 42
> $
>
> If you still have problems, it would be easier if you could paste some code,
> this way I can try to help you more.
>
> HTH,
> Laurent
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel at lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>


More information about the MacRuby-devel mailing list