在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
摘要:我们都知道Objective-C和Cocoa语言可以开发iOS应用,但是一年前,苹果决定在iOS系统上使用Lua语言。Wax框架的想法很简单:凡是Objective-C能做的,Lua也能做!考虑使用像Lua这样一门简单而高效的编程语言,构建原生iPhone应用程序有许多充分的理由,而本文将深入探讨Wax具有的一些好处,同时演示把Lua与Xcode 4和iOS软件开发工具包(SDK)集成起来必不可少的实际步骤。 2011年6月的编程语言排行榜Lua语言一军突起,一举进入前十名。这与一年前苹果决定在iOS系统上使用Lua语言密不可分。但是,你了解如何用Lua语言在iOS上开发应用吗?这里将向各位介绍Lua语言的iOS应用开发框架Wax,其中在iOS平台上无比火爆的《愤怒的小鸟》就是由Lua语言用Wax开发的。全文共分两部分,第一部分将带您深入探讨Wax具有的一些好处,同时演示把Lua与Xcode 4和iOS软件开发工具包(SDK)集成起来必不可少的实际步骤。第二部分将逐步介绍如何用Wax构建一个简单的应用程序,显示Twitter上的当前趋势话题列表,可以用按钮来更新内容。 Wax是什么? Wax for iPhone这种框架在开发时,旨在把Lua脚本语言和原生Objective-C应用编程接口(API)结合起来。这意味着,你可以从Lua里面,使用任何和全部的Objective-C类及框架。 从技术上来讲,Wax结合了Objective-C类和原生C代码。Lua语言嵌入了C语言,然后Objective-C类并入到其中。 为什么使用Wax? Wax是免费的、开源的。与其他一些基于Lua的移动开发解决方案不同,Wax是个开源框架,只需要你花一点点时间就可以上手,不需要花钱。不喜欢Wax的工作方式,或者发现实施方面的缺陷?源代码可免费获取,你总是可以改动源代码,以满足自己的需要。 可以利用原生API。这意味着,为教Objective-C而编写的教程很容易由Lua for Wax来改动和编写。这还意味着,你的应用程序在外观感觉上总是如同原生应用程序,不过又得到了用Lua这种高效脚本语言编写代码可以节省时间的好处。 可以使用Xcode。这意味着,模拟器和设备部署都轻而易举,不会轻易与未来的iOS版本决裂。 可以利用所有现有的Objective-C库。如果你有一个Objective-C类是以前编写的,不需要改动,就可以将它用在Lua中只要把它放入到Xcode。Three20之类的库也是一样。只要按照正常指令来添加库,就可以使用Lua代码访问它们。 可以利用Wax Lua模块。Wax有几个内置的Lua模块,使得异步HTTP请求和JavaScript对象标注(JSON)创建/解析极其容易而快速(因为模块是用C编写的)。 没必要管理内存。不再需要操心内存分配之类的事务。Wax为你处理这一切。 Lua类型自动转换成对应的Objective-C类型,反之亦然。 这意味着,如果你调用了需要NSString和NSInteger的某个方法,但传送了Lua字符串和Lua整数,Wax会为你搞定转换工作。这种转换功能强大,甚至可以处理复杂的Objective-C特性,比如选择器。 你可以利用所有上述特性。不需要精挑细选。你获得所有特性! OK,实在太棒了!我该如何安装Wax? 首先你需要Xcode和iPhone SDK。要是你还没有这些东西,赶紧弄一份! 用Xcode创建项目 我们先创建一个新的基于Windows的项目,名为WaxApplication。别忘了把设备设置成iPhone: 通过Finder浏览到你保存该项目的文件夹。创建三个新的文件夹:wax、scripts和Classes。你的文件夹看起来应该像这样: 设置Wax(第一部分,处理文件) 首先,下载源代码的压缩包。Wax放在GitHub上(https://github.com/probablycorey/wax),那样下载源代码就很容易。从这里下载压缩包。 现在,解压缩刚下载的文件。浏览到刚解压缩的文件夹。它会有probablycorey-wax-124ca46之类的名称。 你的屏幕现在看起来应该像这样: 现在,执行下列操作: ◆ 拷贝lib和bin文件夹,把它们粘贴到位于WaxApplication项目文件夹里面的wax文件夹。 ◆ 将xcode-template/Classes/ProtocolLoader.h拷贝到WaxApplication项目文件夹。 ◆ 拷贝xcode-template/scripts/文件夹,将它放到WaxApplication项目文件夹里面。 ◆ 打开WaxApplication项目文件夹里面的wax/lib/extensions/文件夹。删除SQLite和xml文件夹,下图所示: 你的屏幕应该看起来像这样: 设置Wax(第二部分,配置项目) 现在用Finder选择Classes、scripts和wax文件夹,把它们拖入到Xcode项目中。把它们放到显示Wax Application和1 target, iOS SDK X.X的那个条下方。不要勾选显示Copy items into destination group’s folder(if needed)的复选框。点击完成。 现在点击显示Wax Application和1 target, iOS SDK 4.3的那个条。接着采取下列步骤: ◆ 在右边窗格中,寻找Targets标题,点击WaxApplication。点击Build Phases(构建阶段)选项卡。点击Copy Bundle Resources(复制捆绑资源),清除所有lua文件。 ◆ 在右下角,先点击Add Build Phase(添加构建阶段),再点击Add Run Script(添加运行脚本)。 ◆ 将Shell设成/bin/zsh ◆ 将Shell下面的文本区域设成$PROJECT_DIR/wax/lib/build-scripts/copy-scripts.sh。 你的屏幕现在看起来像这样: 改动main.m 在左边窗格中,打开名为WaxApplication的文件夹。接下来,打开Supporting Files文件夹。接着,打开main.m,把文件的内容换成如下:
//这是发生奇迹的地方! // Wax并不使用nib文件来装入主视图,一切在AppDelegate.lua文件里面完成 #import <UIKit/UIKit.h> #import "wax.h" #import "wax_http.h" #import "wax_json.h" #import "wax_filesystem.h" int main(int argc, char *argv[]) { NSAutoreleasePool * pool =[[NSAutoreleasePool alloc] init]; wax_start("AppDelegate.lua", luaopen_wax_http, luaopen_wax_json, luaopen_wax_filesystem, nil); int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate"); [pool release]; return retVal; } 别忘了保存文件! 删除不必要的文件 删除MainWindow.xib、WaxApplicationAppDelegate.h和WaxApplicationAppDelegate.m三个文件。打开WaxApplication/Supporting Files/WaxPallication-Info.plist,然后删除键是Main nib file base name的那一行。 测试安装的Wax 按↵(命令+回车键),或者按左上角的Run,就可以在模拟器中运行应用程序。要是一切正常,你会看到一个简单的应用程序会说Hello Lua!。 要是你没看到这个消息,检查之前的步骤,看看有没有步骤漏了。 查看Lua 展开Scripts文件夹,打开AppDelegate.lua。你会看到运行该应用程序的Lua代码。 你可能会先注意到,没有语法高亮。遗憾的是,对于Xcode中的Lua语法高亮问题,我还没有发现稳定的解决方案(是你发现了,请留言告诉我!)。 接下来你可能会注意到,没有方括号,不过使用了像UIScreen和UIWindow这些类。那是由于你在使用Lua构建一个AppDelegate类;在苹果和苹果的代码看来,你在使用Objective-C、构建Objective-C类! 方法名称 你可能还注意到奇怪的方法名称colorWithRed_green_blue_alpha。要是你熟悉Objective-C,就知道方法名称可以有冒号。Lua中的函数名称不能有冒号。为了补偿这个差异,凡是Objective-C中隔开方法名称的地方,在Lua中都换成下划线。比如说: Objective-C中的colorWithRed:green:blue:alpha对应于Lua中的colorWithRed_green_blue_alpha。 Objective-C中的selectRowAtIndexPath:animated:scrollPosition:对应于Lua中的selectRowAtIndexPath_animated_scrollPosition。 面向对象的模型 Lua的另一个问题是,它没有像Objective-C那样的继承体系。Lua中根本没有类。为了克服这个问题,Wax突出显示了放在每个Wax Lua文件最前面的一个函数:waxClass。在默认的AppDelegate.lua中,这一行看起来像这样:
waxClass{"AppDelegate", protocols = {"UIApplicationDelegate"}}
想通过Lua创建一个Objective-C类,就要使用waxClass{CLASS NAME, PARENT_CLASS}这个函数。你添加到该Lua文件的所有之后的函数(在同一个文件里面)都会作为实例方法,自动添加到新的类。 AppDelegate.lua的这一行显示,还可以定义你的类定义哪些协议。 虽然waxClass解决了定义Objective-C可以使用的类这个问题,但还是存在一个问题:由于Lua没有类,它没有像Objective-C那样的动态自变量。为了克服这个问题,Wax自动将每个方法的第一个变量作为类的当前实例。你可以发现,当你查看AppDelegate.lua中的applicationDidFinishLaunching时,第一个变量是自变量,即使Objective-C版的这个方法只有1个变量。然而,如果你非得将类的当前实例作为每个方法的第一个变量来传送,就会很烦人,于是添加了一些语法上的便利(syntactical sugar)。不是使用.操作符在Lua中进行方法调用,而是使用了:操作符:
local view = UIView.initWithFrame(CGRect(0, 0, 100, 100))
--以下一模一样
view:addSubview(someView) iew.addSubview(view, someView) 值得一提的另一个重要方面是,Wax不支持Objective-C属性。Wax迫使Lua和Objective-C只与方法进行联系。 -- 这不行 someView.frame -- 你而是需要使用getter/setter方法
View:frame() some View:setFrame(someFrame) 只用于Lua的变量 你可以使用点.操作符,为任何Objective-C对象创建成员变量。不像冒号:操作符(用于对Objective-C类/实例调用方法),点.操作符可以针对对象的Lua方面,动态创建成员变量(对象的Objective-C方面对这些变量一无所知)。在对象的生命周期之内,都可以使用成员变量。 输出到控制台 AppDelegate.lua还显示了你如何可以编写调试文本、输出到控制台。你可以使用函数puts。 内存管理 我之前说过,使用Lua的话,你根本没必要分配、保留和释放内存。你在调用任何初始化器之前,根本不需要调用内存分配。实际上,如果你这么做的话,程序可能会出现内存泄漏。 太棒了!接下来做什么? 你已经深入了解了专门针对Wax的Lua的基本知识,就可以准备编写iPhone应用程序了! 在这个教程的第二个部分,我们将只用几行Lua,就可以编写出拥有刷新按钮的一个Twitter示例应用程序。 看完这个教程是不是你也有信心打造出自己的《愤怒的小鸟》呢?
第四步:UITableViewController数据方法 我们的应用程序可以启动,这很好,但我们想要显示一些数据。为了显示这些数据,所有UITableViewController必须实施几个方法,告诉设备显示什么数据。其中第一个方法是numberOfSectionsInTableView:,它会返回将在表中显示的群组数量。对该应用程序来说,这一步很容易,因为我们只需要一个表段,即拥有当前趋势的那个表段。
function numberOfSectionsInTableView(self, tableView) return1 end 是不是很容易?现在我们得实施tableView_numberOfRowsInSection方法,它告诉设备某个特定的分组会有多少行。对该应用程序来说,这同样很容易,因为我们只有一个表段。记得我们如何用init方法对Lua表进行初始化吗?只要计数该表中的表项数量,就知道该表需要显示多少行:
function tableView_numberOfRowsInSection(self, tableView, section) return #self.trends end 这使用Lua简写方法来计数表中的表项数量。如果你不熟悉Lua表,下面有几个要点: 1. 大多数语言中被称为词典的东西在Lua中被称为表。 2. 大多数语言中被称为数组的东西被称为带有序数字键的表。 3. 数组使用从1开始的索引,而几乎其他每种语言使用从0开始的索引。 接下来是tableView_titleForHeaderInSection方法。该方法告诉设备显示什么作为某群组的标题。你只要返回某个指定群组的字符串,标题之后会神奇地出现在表行上方:
function tableView_titleForHeaderInSection(self, tableView, section) if section == 0 then return "Currently Trending Topics" end return nil end 相当简单。现在我们只要往表格填充从Twitter的服务器取来的数据。如果你熟悉Objective-C中的UITableViewControllers,就会认识这下一个方法:
function tableView_cellForRowAtIndexPath(self, tableView, indexPath) local identifier = "TwitterTableViewControllerCell" local cell = tableView:dequeueReusableCellWithIdentifier(identifier) or UITableViewCell:initWithStyle_reuseIdentifier(UITableViewCellStyleDefault, identifier) local object = self.trends[indexPath:row() +1] -- 必须是+1,因为Lua数组从1开始 cell:textLabel():setText(object) return cell end 这个方法要复杂一点。首先,我们定义了对同一种类型,但可能有不同内容的所有表格单元(cell)来说很独特的标识符。这种情况下,我们称之为TwitterTableViewControllerCell。接下来,我们使用Lua简写方法,获得UITableViewCell的实例。注意夹在这两个方法调用之间的or。如果第一个方法调用的结果不是false或nil, cell就被设成第一个方法调用的值。否则,cell会被设成是第二个方法调用的结果。 我们这么做是为了节省内存。这样一来,设备每次只要为屏幕上10个左右的表格单元分配内存,而不是为数据源里面可能拥有的数千个表格单元分配内存。当然,我们不会有数千行要显示,但这仍是个有必要养成的好习惯。接下来,我们把表格单元的内容设成从self.trends数组的合适部分获取的趋势。我们知道,该索引从来不会超出self.trends的范围,因为我们通过方法tableView_numberOfRowsInSection,将数组大小告诉给了设备。最后,我们返回刚创建的表格单元。如果你现在按Run,它应该看起来像这样: 第五步:从Twitter装入数据 现在说说真正展现Wax魅力的好玩部分:从互联网、或者更准确地说从Twitter的服务器装入JSON数据。先不妨创建一个名为loadDataFromTwitter的新方法。该方法会从Twitter的服务器获取JSON数据,然后为表重新装入新数据。
function loadDataFromTwitter(self) UIApplication:sharedApplication():setNetworkActivityIndicatorVisible(true) -- show spinner wax.http.request{"http://api.twitter.com/1/trends.json", callback =function(json, response) UIApplication:sharedApplication():setNetworkActivityIndicatorVisible(false) -- hide spinner if response:statusCode() == 200 then self.trends = {} -- Reset the list of trends when the trends are refreshed for index,value in ipairs(json["trends"]) do-- iterate over a table with numerical keys table.insert(self.trends, value["name"]) -- append the value to the "array" end end self:tableView():reloadData() end} end 是的,就这么简单。你定义了请求的URL以及请求完毕后执行的回调。Wax自动确认服务器在运行JSON后,会将JSON文本自动转换成Lua表。这使得显示网络活动指示器(设备右上角靠近无线信号指示器的图标)异常容易。返回的JSON看起来像这样。键trends保存一组对象,这些对象包含趋势名称和访问提到该趋势的所有Twitter消息的URL。 趋势名称存储到self.trends变量里面后,重新装入tableView,它可以再次调用我们之前定义的所有数据方法。这导致趋势在表中显示,非常类似最终产品。 如果你立即试图运行该应用程序,看上去没什么不同。那是因为该方法从未调用。如果从viewDidLoad:里面调用该方法,我们就能确保总是可以显示最新趋势。把这行添加到viewDidLoad:方法末行的前一行: self:loadDataFromTwitter() 如果你点击Run,应用程序看起来有点像这样(你得等几秒钟装入趋势,请留意那个活动指示器!): 第六步:添加重新装入按钮 该应用程序相当棒。不过,要是有重新装入按钮让你可以显示最新趋势,就更好了。幸好,这很容易实现。 不妨把重新装入按钮放到屏幕的右上角。苹果其实提供了上面有刷新图标的按钮,以图方便,就用这个按钮吧。先开始用viewDidLoad:方法创建一个按钮。把下面这行添加到loadDataFromTwitter调用的前面。
local button = UIBarButtonItem:initWithBarButtonSystemItem_target_action(UIBarButtonSystemItemRefresh, self, "loadDataFromTwitter")
这创建了一个UIBarButtonItem:一旦按下按钮,就会对当前对象实例调用loadDataFromTwitter方法。如果你想尝试其他风格。 我们已创建好了按钮,现在需要把它添加到我们的界面上。使用UITableViewController使得这项工作轻而容易,我们只要对导航栏对象实例调用setRightBarButtonItem:方法,就像这样(这行位于上面给出的那一行后面): self:navigationItem():setRightBarButtonItem(button) 如果你各方面都做好了,完成的应用程序应该看起来像这样: 第七步:额外好处 这个项目一个有意思的扩展就是制作更显眼的装入指示器。这可能需要把UIActivityIndicatorView放到其中一个按钮位置。 结论 我希望你觉得这篇教程深入浅出地介绍了Wax。如果你想看到有关某个课题的更多Wax教程,欢迎给我留言。 |
请发表评论