通过Ruby中的FFI调用objective-c选择器方法

时间:2013-06-06 15:30:30

标签: objective-c ruby macos ffi

假设您要打开一个简单的警告框,它在objective-c Universe中将类似于:

NSAlert *alert = [[[NSAlert alloc] init] autorelease];
    [alert setMessageText:@"Alert."];

    [alert beginSheetModalForWindow:window
                      modalDelegate:self
                     didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
                        contextInfo:nil];

beginModalForWindow被定义为选择器方法。在苹果参考指南中,它的全名是“beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:”

它在NSAlert.h中定义为:

- (void)beginSheetModalForWindow:(NSWindow *)window modalDelegate:(id)delegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo;

现在这个简单的问题,如何在ruby ffi中定义这个方法?

module AppKit
  extend FFI::Library

  # Load the Cocoa framework's binary code
  ffi_lib '/System/Library/Frameworks/AppKit.framework/AppKit'

  attach_function :beginSheetModalForWindow, [:pointer,:pointer,:pointer], :bool
end

失败:

An exception occurred running ffi-test.rb
  Function 'beginSheetModalForWindow' not found in [/System/Library/Frameworks/AppKit.framework/AppKit] (FFI::NotFoundError)

1 个答案:

答案 0 :(得分:3)

总之,你不能。至少没有一圈箍跳。

attach_function按照说法行事;它将 C函数绑定到Ruby运行时。 beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:不是C函数;它是一个选择器。

您真正想要绑定的是该选择器的实现

但不是真的。

您真正想要绑定的是objc_msgSend,其类型签名包含该方法的所有参数。而且你还需要附上sel_getUid。哦,你需要附上objc_lookUpClass

然后你会做类似(伪代码)的事情:

 ... attach objc_msgSend to msgSend with no arguments and object return type ...
 alert = msgSend(msgSend(msgSend(lookupClass("NSAlert"),getUid("alloc")),
           getUid("init")), getUid("autorelease"))

 ... attach objc_msgSend to bs with all the arguments for beginSheetModal....
 bs(alert, getUid("beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo", ... all the arguments ...))

或类似的东西。

此时,您重新发明了一种非常基本的MacRubyRubyCocoa形式。