iMRuby
iMRuby是ObjC(Objective-C)与Ruby(Mruby)之间的桥梁
示例
// objective-c file
@interface MRBViewController ()
@property (nonatomic, strong) MRBContext *context;
@end
@implementation MRBViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 执行view.rb
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"view" ofType:@"rb"];
NSString *script = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
self.context = [[MRBContext alloc] init];
self.context.exceptionHandler = ^(NSError * _Nonnull exception) {
NSLog(@"%@", exception.userInfo[@"msg"]);
};
[self.context registerConst:@"Target" value:self];
[self.context evaluateScript:script];
MRBValue *superView = [MRBValue valueWithObject:self.view inContext:self.context];
[self.context callFunc:@"create_view" args:@[superView]];
}
- (void)touchAction:(id)sender
{
[self.context evaluateScript:@"showAlertView"];
}
@end
# ruby file
require_cocoa "UIColor"
require_cocoa "UIView"
require_cocoa "UIImage"
require_cocoa "UIImageView"
require_cocoa "UILabel"
require_cocoa "UIFont"
require_cocoa "UIButton"
require_cocoa "UIAlertController"
require_cocoa "UIAlertAction"
def create_view(super_view)
# Clolors
red = UIColor.redColor
blue = UIColor.blueColor
green = UIColor.greenColor
yellow = UIColor.yellowColor
white = UIColor.whiteColor
super_view.setBackgroundColor_(white)
# UIlabel
super_view.addSubview_(createLabel("UILabel", 40, 64))
label = UILabel.alloc.init
label.setFrame_({'x' => 200, 'y' => 64, 'width' => 150, 'height' => 50})
label.setText_("Hello World!")
label.setFont_(UIFont.systemFontOfSize_(24))
label.setTextColor_(red)
label.setBackgroundColor_(green)
super_view.addSubview_(label)
# UIView
super_view.addSubview_(createLabel("UIView", 40, 120))
blue_sub_view = UIView.alloc.init
blue_sub_view.setFrame_({'x' => 200, 'y' => 120, 'width' => 100, 'height' => 100})
blue_sub_view.setBackgroundColor_(blue)
super_view.addSubview_(blue_sub_view)
# UIImageView
super_view.addSubview_(createLabel("UIImageView", 40, 230))
image = UIImage.imageNamed_("logo")
image_view = UIImageView.alloc.initWithImage_(image)
image_view.setFrame_({'x'=>200, 'y'=> 230, 'width' => 100, 'height' => 100})
super_view.addSubview_(image_view)
# UIButton
super_view.addSubview_(createLabel("UIButton", 40, 340))
btn = UIButton.buttonWithType_(0)
## MRBCocoa::Const::Target is defined in OC
## [self.context registerConst:@"Target" value:self];
## “self” is the view controller
## "touchAction:" action is defined in OC and execute ruby script to show alert
btn.addTarget_action_forControlEvents_(MRBCocoa::Const::Target, "touchAction:", 1<<6)
btn.setTitle_forState_("Click to show alert", 0)
btn.setFrame_({'x' => 200, 'y' => 340, 'width' => 170, 'height' => 50})
btn.setBackgroundColor_(red)
super_view.addSubview_(btn)
end
def showAlertView
# UIAlertController
completion = Proc.new {puts "show..."}
completion_block = completion.to_cocoa_block("void,void")
alertController = UIAlertController.alertControllerWithTitle_message_preferredStyle_("iMRuby", "Hello World!", 1)
ok_handler = Proc.new {|action| puts "OK..."}
ok_handler_block = ok_handler.to_cocoa_block("void, UIAlertAction")
okAction = UIAlertAction.actionWithTitle_style_handler_("OK", 0, ok_handler_block)
alertController.addAction_(okAction)
MRBCocoa::Const::Target.presentViewController_animated_completion_(alertController, true, completion_block)
end
def createLabel(text, x, y)
label = UILabel.alloc.init
label.setFrame_({'x' => x, 'y' => y, 'width' => 150, 'height' => 50})
label.setText_(text)
label.setFont_(UIFont.systemFontOfSize_(24))
label.setTextColor_(UIColor.blackColor)
return label
end
安装
iMRuby通过CocoaPods提供。要安装它,只需将以下行添加到Podfile中
pod 'iMRuby'
类UML
使用
iMRuby的使用类似于iOS中的JavascriptCore。文章链接
Ruby和OC互相转换
OC类型 | Ruby类型 |
---|---|
nil | nil |
NSString | 字符串,符号 |
NSNumber | 整数字面量、浮点数、布尔值 |
NSDictionary | 哈希 |
NSArray | 数组 |
NSDate | 时间 |
NSBlock | 包装对象 Ruby 类 MRBCocoa::Block |
id/NSObject | 包装对象 Ruby 类 MRBCocoa::Object |
类 | 包装对象 Ruby 类 MRBCocoa::Klass |
CGPoint | 特定的哈希 {'x' => 1, 'y' => 1} |
CGSize | 特定的哈希 {'宽度' => 1, '高度' => 1} |
CGRect | 特定的哈希 {'x' => 1, 'y' => 1, 'width' => 1, 'height' => 1} |
NSRange | 特定的哈希 {'位置' => 0, '长度' => 1} |
MRBContext
实例
创建 self.context = [[MRBContext alloc] init];
运行 Ruby 脚本
[self.context evaluateScript:@"puts \"Happy Niu year!\""];
# => Happy Niu year!
捕获异常
self.context.exceptionHandler = ^(NSError * _Nonnull exception) {
NSLog(@"%@", exception.userInfo[@"msg"]);
};
[self.context evaluateScript:@"happy_nui_year(2021)"];
# => undefined method 'happy_niu_year' (NOMethodError)
注册常量
[self.context registerConst:@"Niu" value:@"Happy Niu year!"];
[self.context evaluateScript:@"puts MRBCocoa::Const::Niu"];
# => Happy Niu year!
注册方法
[self.context registerFunc:@"happy_niu_year" block:^NSInteger(int a, int b){
NSLog(@"start...");
int sum = a + b;
NSLog(@"finish...");
return sum;
}];
MRBValue *sum = [self.context evaluateScript:@"MRBCocoa.happy_niu_year(2020,1)"];
NSInteger niu_year = sum.toInt64;
NSLog(@"%@", @(niu_year));
# => start...
# => finish...
# => 2021
通过下标注册方法或常量
self.context[@"Niu"] = @"Happy Niu year!";
self.context[@"happy_niu_year"] = ^NSInteger(int a, int b) {
NSLog(@"start...");
int sum = a + b;
NSLog(@"finish...");
return sum;
};
MRBValue *sum = [self.context evaluateScript:@"puts MRBCocoa::Const::Niu;MRBCocoa.happy_niu_year(2020,1)"];
NSInteger niu_year = sum.toInt64;
NSLog(@"%@", @(niu_year));
调用OC Ruby方法
# ruby.rb
def test_func(str)
puts str
end
MRBValue *strParam = [MRBValue valueWithString:@"hello" inContext:self.context];
[self.context callFunc:@"test_func" args:@[strParam]];
调用Ruby OC对象方法
// OC .m file
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (NSString *)say_something:(NSString *)message;
- (void)coding:(NSString *)code finished:(BOOL(^)(NSString *name, int age))finished;
@end
@implementation Person
- (NSString *)say_something:(NSString *)message
{
NSLog(@"%@", message);
return [self.name stringByAppendingString:message];
}
- (void)coding:(NSString *)code finished:(BOOL(^)(NSString *name, int age))finished
{
NSLog(@"I am coding %@", code);
if (finished) {
BOOL f = finished(self.name, self.age);
NSLog(@"Block return value: %@", @(f));
}
}
@end
# ruby .rb file
require_cocoa 'Person'
person = Person.alloc.init
person.setName_('anan')
person.setAge_(2)
message = person.say__something_("happy Niu year!")
puts message
finished = Proc.new {|name, age| puts "I am #{name}, #{age} year old"; true}
finished_block = finished.to_cocoa_block("BOOL, NSString *, int");
person.coding_finished_("Ruby", finished_block)
调用OC方法规则
- 将':'替换为'_'
- (NSString *)saySomething:(NSString *)message;
message = person.saySomething_("happy Niu year!")
- 将'_'替换为'__'
- (NSString *)say_something:(NSString *)message;
message = person.say__something_("happy Niu year!")
- 如果'_'或'__'在OC方法名称的开头,不做任何操作
- (NSString *)__say_something:(NSString *)message;
message = person.__say__something_("happy Niu year!")
- 如果OC方法有NSBlock参数,可以使用
to_cocoa_block
将Proc
转换为NSBlock
- (void)coding:(NSString *)code finished:(BOOL(^)(NSString *name, int age))finished;
// ruby
finished = Proc.new {|name, age| puts "I am #{name}, #{age} year old"; true}
finished_block = finished.to_cocoa_block("BOOL, NSString *, int");
person.coding_finished_("Ruby", finished_block)
注意
如果你在Apple硅芯片(M1)的模拟器上运行示例,您需要在“构建设置”->“架构”->“排除架构”中添加arm64给任何iOS模拟器SDK尝试添加arm64到构建设置 -> 架构 -> 排除架构,以适用于任何iOS模拟器SDK。
作者
ping.cao
许可协议
iMRuby可在MIT许可证下使用。有关更多信息,请参阅LICENSE文件。