如何混合 Objective-C 与 Ruby 编程

Python012

如何混合 Objective-C 与 Ruby 编程,第1张

关于 RubyMotion 我已经写过很多文章了,但如何混用Objective-C与Ruby还从未涉及到。实际上你能在RubyMotion项目中使用Objective-C代码,也可以在传统Objective-C的App中使用Ruby代码。也许你一次听来觉得像黑魔法一样,所以来一起看看下面这些示例。

Objective-C in Ruby

很多iOS开发者在现有的Objecive-C代码中存在大量深层的备份日志,这样就为项目手动转换为 RubyMotion 带来了很大的痛苦。然而幸运的是现在仅仅只需要将编译好的Objective-C代码添加到我们的新RubyMotion应用里。

比如我们要在 RubyMotion 应用中使用 Objective-C 形式的 CYAlert 类:

// CYAlert.h

#import <UIKit/UIKit.h>@interface CYAlert : NSObject

+ (void)show@end

// CYAlert.m

#import "CYAlert.h"

@implementation CYAlert

+ (void)show {

UIAlertView *alert = [[UIAlertView alloc] init]

alert.title = @"This is Objective-C"

alert.message = @"Mixing and matching!"

[alert addButtonWithTitle:@"OK"]

[alert show]

[alert release]

}@end

为了在RubyMotion应用中能正常使用,这里需要将CYAlert的两个文件都放置到类似 ./vendor/CYAlert 里面,然后在 Rakefile: 中告诉 RubyMotion 使用vendor_project创建那个文件夹。

Motion::Project::App.setup do |app|

# Use `rake config' to see complete project settings.

app.name = 'MixingExample'

# ./vendor/CYAlert contains CYAlert.h and CYAlert.m

app.vendor_project('vendor/CYAlert', :static)

end

那么现在在 AppDelegate 中,我们这样使用 CYAlert:

class AppDelegate

def application(application, didFinishLaunchingWithOptions:launchOptions)

CYAlert.show

true

end

end

如果你编译本文档中的脚本有困难。那么你不是一个人在战斗。记住重要原则: 以编译你的perl的方式,编译你的程序(不好意思)

并且,每一个使用了Perl的C程序都必须连接 perl 库. 你问,那是什么?Perl本身就是用C写的。Perl库是编译过的用来创建Perl可执行程序的C程序集合 (/usr/bin/perl或者类似的)。(结论:你不能在没有编译过或者正确安装过Perl的系统中编译嵌入了Perl的C程序。不正确安装指,只是复制了Perl的二进制可执行文件而没有复制perl的库目录。)

当你在C中使用Perl时, 你的C程序将要--通常--需要创建、执行,然后销毁一个 Perl解释器

如果你的Perl烤备足够新到包含本文档 (版本5.002或者更新), 则Perl库 (以及你将需要的EXTERN.h 和 perl.h) 将会在类似这样的目录中:

/usr/local/lib/perl5/your_architecture_here/CORE

或者可能就直接是

/usr/local/lib/perl5/your_architecture_here/CORE

或者可能类似

/usr/opt/perl5/CORE

执行这个表达式可以定位这个目录:

perl -MConfig -e 'print $Config{archlib}'

这是如何编译下节的示例代码:往你的C程序中墙加一个Perl解释器的语句,在我的linux机器上是这样:

% gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include-I/usr/local/lib/perl5/i586-linux/5.003/CORE-I/usr/local/lib/perl5/i586-linux/5.003/CORE-o interp interp.c -lperl -lm

(那些是一行。) 在我跑着老版本perl 5.003_05的DEC Alpha机器上, 有一点点儿不一样:

% cc -O2 -Olimit 2900 -DSTANDARD_C -I/usr/local/include-I/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE-L/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE -L/usr/local/lib-D__LANGUAGE_C__ -D_NO_PROTO -o interp interp.c -lperl -lm

你如何找出需要填加什么?如果你的Perl版本高于5.001, 执行 perl -V 命令,注意一下它的有关``cc'' 和 ``ccflags''的信息。

你将不得不根据你的系统选择适当的编译器。 (cc,gcc,等等。) : perl -MConfig -e 'print $Config{cc}' 将告诉你用什么。

你还得根据你的系统选择正确的Perl库目录 (/usr/local/lib/...) 如果你的编译器抱怨有函数未定义,或者它不能定位 -lperl,则你需要用-L手动指定库文件路径。如果它抱怨找不到EXTERN.h 和 perl.h,你需要用-I手动指定头文件路径。

你可能还需要如法增加其它库哪些?可能包括这行指令打印出的这些:

perl -MConfig -e 'print $Config{archlib}'

如果你的perl配置安装无误,则模块ExtUtils::Embed将为你检测出你需要的所有信息。

% cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

如果ExtUtils::Embed不包含在你的perl发行版中, 你可以从http://www.perl.com/perl/CPAN/modules/by-module/ExtUtils/.下载它 (如果本文档来自你的perl发行版,则你已经安装了perl 5.004或者更高的版本,并且你肯定有这个模块)

CPAN上的ExtUtils::Embed,包括了有关本文档的所有的源码、测试以及其它对你有用的示例程序。

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

往你的C程序中增加一个Perl解释器

通常, perl (C源程序) 是一个嵌入Perl(语言)的好例子,所以我将示范一个miniperlmain.c。当然,它不太标准,也不太精巧。

#include <EXTERN.h> /* from the Perl distribution */#include <EXTERN.h> /* from the Perl distribution */

static PerlInterpreter *my_perl /***The Perl interpreter***/

int main(int argc, char **argv, char **env){my_perl = perl_alloc() perl_construct(my_perl) perl_parse(my_perl, NULL, argc, argv, (char **)NULL) perl_run(my_perl) perl_destruct(my_perl) perl_free(my_perl) }

注意我们没有使用env指针。通常做为perl_parse的最后一个选项, env参数为NULL,代表使用当前的环境变量。

现在编译它 (我将叫它 interp.c)为可执行文件:

% cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

经过成功的编译以后, 你将可以像使用perl一样地使用interp。

% interpprint "Pretty Good Perl \n" print "10890 - 9801 is ", 10890 - 9801 <CTRL-D> Pretty Good Perl 10890 - 9801 is 1089

或者

% interp -e 'printf("%x", 3735928559)'deadbeef

你也可以通过在调用perl_fun以前替换argv[1],在C程序里从一个文件中读取然后执行Perl代码。

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

在你的C程序中调用一个Perl子程序

为了调用独立的Perl子程序,你可以使用任何<al>perlcall手册</al>中的perl_call_*类型的函数。在此例中,我们将使用perl_call_argv。

它们将在下面这个我叫做showtime.c的程序中中展现。

#include <EXTERN.h> #include <perl.h>

static PerlInterpreter *my_perl

int main(int argc, char **argv, char **env){char *args[] = { NULL } my_perl = perl_alloc() perl_construct(my_perl)

perl_parse(my_perl, NULL, argc, argv, (char **)NULL)

/*** skipping perl_run() ***/

perl_call_argv("showtime", G_DISCARD | G_NOARGS, args)

perl_destruct(my_perl) perl_free(my_perl) }

showtime是一个Perl子程序,它没有参数。 (它是 G_NOARGS) 并且我将忽略它的返回值 (它是G_DISCARD). 那些标记,以及其它一些标记,在perlcall手册中。

我将在文件<el>showtime.pl</el>中定义showtime函数:

print "I shan't be printed."

sub showtime {print time }

足够简单。现在编译并运行:

% cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

% showtime showtime.pl818284590

返回从1970年1月1日 (Unix epoch开始时间)到现在我正在执行程序语句的时间。

在这一部分,我们不需要调用perl_run,但是通常好的编程习惯来保证正确的使用库是好的,包括对所有的对象调用 DESTROY 方法以及块的END {}。

如果你想传递参数给Perl函数,你可以填加以NULL结尾的args表给perl_call_argv。为了其它数据类型或者测试返回值,你将需要操作Perl的栈。那将在本文档最后的章节进行讨论: 在你的C程序中操作Perl的栈。

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

在你的C程序中执行一个Perl表达式

Perl提供了两个API函数来执行Perl代码片断。它们是perl_eval_sv 和 perl_eval_pv。

经常的情况是,你只需要在程序中执行一块的Perl代码。它可以如你所愿的长:它可以包括 use、require和 do 用来包含其它的外部Perl文件。

perl_eval_pv 可以让我执行单独的Perl语句,然后把执行结果变量返回成C的形式。下面的程序 string.c,执行三个Perl语句, 从第一个取得int,从第二个取回float, 从第三个取回char *。

#include <EXTERN.h> #include <perl.h> static PerlInterpreter *my_perl int main(int argc, char **argv, char **env) { STRLEN n_a char *embedding[] = { "", "-e", "0" } my_perl = perl_alloc() perl_construct(my_perl) perl_parse(my_perl, NULL, 3, embedding, NULL) perl_run(my_perl) /** Treat $a as an integer **/ perl_eval_pv("$a = 3$a **= 2", TRUE) printf("a = %d\n", SvIV(perl_get_sv("a", FALSE))) /** Treat $a as a float **/perl_eval_pv("$a = 3$a **= 2", TRUE) printf("a = %f\n", SvNV(perl_get_sv("a", FALSE))) /** Treat $a as a float **/ perl_eval_pv("$a = 'rekcaH lreP rehtonA tsuJ'$a = reverse($a)", TRUE) printf("a = %s\n", SvPV(perl_get_sv("a", FALSE), n_a)) perl_destruct(my_perl) perl_free(my_perl) }

所有这些名字中含有 sv 的这些怪函数都帮助把Perl的标量返回成C的类型。它们在 perlguts手册里有描述。

如果你编译并运行string.c,你将看到 SvIV()创建整数,SvNV()创建浮点数,以及SvPV()创建一个字符串:

a = 9 a = 9 a = Just Another Perl Hacker

在上例中,我们创建了一个全局的变量来保存我们表达式执行的结果。在很多情形下,通过perl_eval_pv()的执行结果来取回返回值,也不错。如:

... STRLEN n_a SV *val = perl_eval_pv("reverse 'rekcaH lreP rehtonA tsuJ'", TRUE) printf("%s\n", SvPV(val,n_a)) ...

这种方法,我们通过不创建全局变量以及简化我们的化码,避免了名字空间污染。