本文讨论用Lua创建iOS应用的3种方法。包括用Lua创建完整的应用(Corona)一直到用Lua作为app中的脚本元素(通过Wax或diy)。在此之前,我们需要问自己两个问题:
1、为什么要使用Lua?
2、苹果允许使用Lua吗?
这两个问题是紧密相关的。
如果你在此之前对Lua一无所知,我会简单介绍一下Lua。如果你熟悉Lua,则可以跳过这部分内容。
关于Lua
Lua是一个高效、轻量级、嵌入式脚本语言。类似Javascrip、Ruby或Python。有许多和我一样的使用者,都认为Lua是一种简洁优雅的语言。
Lua始于1993年巴西里约热内卢宗铎天主教大学的Roberto Ierusalimschy,Waldemar Celes 和Luiz Henrique de figueiredo。它应用在Mi Casa Verde, Adobe Lightroom, Celestia, lighttpd,LuaTeX, nmap,Wireshark,思科的Adaptive Security Appliance, pbLua,以及成千上万的游戏中(包括 Grim Fandango(神通鬼大),魔兽世界,Tap Tap Revenge(劲乐团)等)。Lua采用MIT协议——这意味着Lua基本无碍于商业和非商业目的。
Lua的主要数据构建机制是表——可变数组和哈希表的结合。列表1列出了一个Lua Table,假设我们用于描述汽车和它的每加仑行驶里程数。我们可以用字符串类型的键来储存车辆信息,如license和make。而采用数字下标索引的方式存储一系列的每加仑里程数。
列表 1: 一个 Lua Table类型
car_data = { license = 'XVW1942',make = 'Volvo', model = 'XC70',30, 31.3,204)">32.4,204)">34.0}
print(car_data[1]) -- 30
['license']) -- XVW1942
(car_data.license) -- XVW1942 (also!)
在Lua中,数组下标从1开始,而不是0。'--'表示注释开始直至行末。让你想不到的是,Lua既不是面向对象的语言,也不是函数编程语言。但是,它也提供了几种机制允许你定制自己的高级特性。Lua中内包含了各种不同的对象系统如:传统OO系统和无类化OO系统(比如Oracle的Self语言和Io语言。译者注* 这两种都是基于范型的语言)。Lua支持“first class 函数”(译者注*其实是匿名函数, Lua可以在运行时随时构造出一个函数,并把它看作一个对象),闭包以及元特性(如元表和元方法)。Lua能很好地满足函数式编程的需要。
关于Lua 的面向对象编程的介绍,请阅读《Lua 编程》(这本书很好地介绍了Lua的方方面面)。你也应当阅读Luawiki上的Lua示例。
列表2列出了链表类的一种实现。变量List实际上是table,用作所有链表对象的元表。它实现了一种向后查找表索引的类似于类的事件处理机制。"List.__index=List"一行允许我们为List对象创建方法。方法是保存在List元表中的函数。当我们调用list对象的这些函数时,将在List元表中查找这些函数的定义并运行。
这段代码显示了Lua的一系列增强特性:多赋值(以及函数返回多个返回值),方法调用语法糖(':' 符号的作用,类似于在函数调用中增加了一个self参数,这在许多语言,从Python到OC都能见到)。
列表 2: Linked List 类
List = {}
List.__index = List
function List:new()
local l = { head = {},tail = {},size = 0 }
l.head.__next,l.tail.__prev = l.tail,l.head
return setMetatable(l,self)
end
function List:first()
if self.size > 0 then
returnself.head.next
else
return nil
end
function List:addFirst(elem)
local node = { prev= self.head,value = elem,
next = self.head.next }
node.next.prev =node
self.head.next =node
self.size =self.size + 1
mylist = List:new()
mylist:addFirst(12)
print(mylist:first())
在这里,我忽略了一些重要的和有趣的东西(比如闭包)。但至少,你已经学到了一点Lua的皮毛。在后面我们进入iPhone编码的时候,会看到更多的Lua代码。更多关于Lua的介绍,请阅读这个网站。
iOS支持脚本吗?
正如本文开头列出的两个问题,尤其是第2个问题:“iPhone允许使用Lua(或其他解释型语言)吗?”毕竟,早在苹果的IDP许可协议中就已经阐明“只有苹果官方的API和内置解释器所支持的解释型代码能被下载或用于app中”。
事实上,本文的拟写大纲时,苹果已经改变了原来禁止开发者在app使用除OC和Javascript(Javascript能在web app或者本地 app中使用——通过UIWebView)以外的其他语言的条款(Circa2010 四月)。最近(2010 九月),苹果再次改变了这个条款,允许使用脚本语言。
但仍然有几个重要的限制。更主要的是,虽然你可以使用Lua(及其它脚本语言),但你的app不能允许用户从web上下载插件(用过应用程序购买吗?),也不能允许用户编写脚本、下载脚本等。有大量的商店应用在使用Lua这样的语言(比如劲乐团)。
当然,在app中包含Lua这类语言的两个最为主要的作用,就是创建插件系统,让用户自己能够编写脚本。除此之外还有许多。
如何在iOS开发中使用Lua?
尽管你不能为终端用户创建一个插件系统,也不能让用户自己编写脚本,但你仍然能以一种插件的方式开发你的系统!这可以加快原型的开发速度,同时在下个版本中有助于添加新的功能。使用Lua还有另一种好处,它允许你进行“快速原型开发”(我最喜欢抱怨的一句话:不要闭门造车式地编程),缓解甚至不需要内存管理,允许更多的团队成员参与到开发中来(有许多Lua项目根本没有程序员在编写代码),应用程序优化更加轻松,提供更强劲的持久化机制。
简而言之,Lua节省了开发时间,降低了开发门槛。生活变得如此轻松愉快!假设你已经决定使用Lua,那么我们该如何做起呢?
Corona
Ansca Mobile公司的Corona允许你完全用Lua来开发iOS应用,以及Android应用。你可以用同样的源代码编译出iOS和Android程序。这正是Lua(实际上是Corona)为何如此吸引人的原因:跨平台。
列表3: Swirly Text 应用中的main.lua
local w,h = display.stageWidth,display.stageHeight
local dx,dy,dtheta = 5,5,5
local background = display.newRect(0,w,h)
background:setFillColor(255,255,255)
local message = display.newText('Hello from Corona',w/2,h/2)
message:setTextColor(0,200)
local function update(event)
local counter_spin =false
message:translate(dx,dy)
message:rotate(dtheta)
if message.x > wor message.x < 0 then
dx= -1 * dx
counter_spin = true
end
if message.y > hor message.y < 0 then
dy = -1 * dy
if counter_spin then
dtheta = -1 * dtheta
Runtime:addEventListener('enterFrame',update)
Corona程序可以用任何文本编辑器开发——我用的是Emacs。所有Lua源代码中用到的资源(图片、声音、数据)必需放在同一个目录,Corona需要main.lua文件来启动app。可以在Corona模拟器中测试代码(支持Intel cpu和Power pc的Mac)。图1显示了我的corona IDE: Emacs(包含Lua文件出口和项目窗口)、Corona终端(可以从诊断中打印调试信息),以及Corona模拟器。
图 1: 我的 Corona 'IDE'
从左至右(反时针方向):Corona模拟器、Emacs的两个窗口(源代码窗口和项目目录窗口)、Corona 终端(输出调试信息)。
想要在物理硬件上运行程序,使用Corona 模拟器的Openfor Build命令。要以iOS编译,你应该提供一个Provisioning Profile(开发或部署)——没错,你并不需要IDP帐号——这两个文件随同app代码和资源一同上传到Ansca公司的服务器上,然后将编译结果返回给你。要以Android编译,你应当有适当的签名证书。然后随着编译过程把你的代码传到Ansca的服务器上。你不必安装AndroidSDK。我没有太深入地研究,但编译后的.apk文件和.app文件已经包含了所有你Lua代码以某种方式处理过的东西。短暂的查看后表明,那不是标准的编译后的Lua字节码,但应该是类似的格式。
Corona事件系统可以处理触摸(包括多点触摸),访问GPS和加速器,处理动画以及自定义事件。它还有一个强大的图形系统,允许你绘制圆、矩形和文本。最近还增加了折线,允许你绘制多边形。你可以显示图片。Corona允许你把这些对象组合在一起然后对他们进行变换。列表4,摘自太阳系模拟器的代码片段,展示了组合多个图形对象的简单例子。其他Corona支持的特性还包括声视频播放,加密算法库,LuaSocket网络库,sqlite存取库Luasqlite等。还能访问本地组件包括textfield、alert和activityindicator。你还可以用webview做诸如登录屏幕之类的事情,有一个示例程序提供了一个库,可以连接到Facebook。我最近看到有一个游戏(很贵)使用了Box2D物理引擎、角色和一些OpenFeint的功能(类似排行榜)。
列表 4: 太阳系应用代码片段
function new(params)
local color =params.color or planet_colors[random(#planet_colors)]
local radius =params.radius or planeTradius()
local planet =display.newGroup()
planet.theta = 0
local x = params.x -ox
local y = params.y -oy
planet.orbital_radius = sqrt(x*x+y*y)
local body =display.newCircle(x + ox,y + oy,radius,radius)
body:setFillColor(unpack(color))
planet:insert(body,true)
planet.body = body
planet.delta_theta =(40/planet.orbital_radius) * 0.1
return planet
把table作为函数参数传递,可以使用命名参数并提供默认值。因此会有 local radius = params.radius orplaneTradius() 这样的写法。
Corona为你做了许多,但同时也有许多不足。最大的问题是对本地控件的访问限制。由于Corona模拟器的限制,它对本地控件的访问是糟糕的。在模拟器中,本地alerts和activityindicators用OSX equivalents实现而不是iOS widgets实现的。textfield、textBox以及web popups在模拟器运行时是不可用的。这在开发时让人痛苦。
最后,除了ANSCA标准以外,无法访问O-CAPI。不仅是大量的标准库,而且第3方库也无法使用,如Three20或 mobileads 这样的APIs。当然,随着Corona Android版本的发布,你可能不想访问OC API因为它限制你的应用程序跨平台的能力(或者增加了复杂性)。最好是通过Lua的CAPI来扩展,就像是许多跨平台的项目一样。
我在ANSCA 小组在他们论坛上的讨论非常有用。随着2.0版本(2010.9)的发布,Corona向每位开发者每年收取249美金。对于游戏版,收费每开发者每年349美金。Ansca公司的网站暗示游戏版的价格只是预览版的。这意味着当正式版发布时价格将会更高。
很容易把Lua解释器放到iOS app中。打开一个Xcode项目把Lua的源文件(除lua.c和luac.c命令行程序外)加到项目中。编译。你就可以使用标准的LuaC API去创建一个解释起并运行源代码,就像 iLua所做的。你可以在 http://github.com/profburke/ilua下载这个示例代码。iLuaShell是一个简单的view-based application,它提供两个文本框给用户——一个给用户输入Lua代码,另一个是不可编辑的,仅仅是显示Lua代码计算的结果。
这个工作用evaluate方法完成,如列表5所示。在方法中首先获取第1个文本域的值,把它交给Lua解释器解析和执行,然后把Lua输出结果放到第2个文本域中。
列表 5 evaluate 方法
-(void)evaluate {
int err;
[input resignFirstResponder] lua_settop(L, 0) err = luaL_loadstring[input.text
cStringUsingEncoding:NSASCIIStringEncoding]) if (0 != err) output.text = [Nsstring stringWithCString:lua_tostring-1)
encoding lua_pop return}
= lua_pcall0int nresults =lua_gettop(L== nresults= @"<no results>"} else Nsstring *outputNS [Nsstring stringforint i = nresults; i > ; i-- outputNS [outputNsstringByAppendingFormat:@"%s " lua_tostring1* i)] = outputNS 注意错误的捕捉和处理被极度简化了。一个好的Luashell应该要做得更多一些,并不是随随便便就可以往苹果商店中放的…
这样做的弊端在哪里?最大的问题是到OC的桥接丢失了。理想状态下我们应该从Lua中调用OC方法,以及相反方向的调用。Lua中要是能实现代码回调和委托方法就太爽了。
CoreyJohnson的iOS Wax最吸引人的地方是在Lua和OC间实现了相互调用。通过Wax,你能轻易用Lua继承一个OC类!列表6展示用扩Wax实现对viewController类的扩展。这段代码是DIY小节中提及的app的一部分。
列表 6:RootViewController.lua
waxClass{'RootViewController',UI.ViewController }
function init(self)
self.super:init()
self.input =UI.TextView:initWithFrame(CGRect(20,20,280,114))
self.output =UI.TextView:initWithFrame(CGRect(20,184,225))
localevalButton = UI.Button:buttonWithType(UIButtonTypeRoundedRect)
evalButton:setTitle_forState('Evaluate',UIControlStateNormal)
evalButton:setFrame(CGRect(200,142,100,32))
evalButton:addTarget_action_forControlEvents(self,'eval:',51); font-family:Arial; font-size:14px; line-height:26px"> UIControlEventTouchUpInside)
self.evalButton = evalButton
self:view():addSubview(self.input)
self:view():addSubview(self.output)
self:view():addSubview(self.evalButton)
return self
function eval(self,sender)
self.input:resignFirstResponder()
local code,errmsg = loadstring(self.input:text())
if not codethen
self.output:setText(errmsg)
return
end
local success,result = pcall(code)
print('resultis ' .. tostring(result))
if not successthen
self.output:setText('Error: ' .. tostring(result))
else
self.output:setText(tostring(result))
end
waxClass函数实际定义了一个新的OC类。本例中我们定义了一个名为RootViewController的类派生自UIViewController。(在Wax中,该OC类被放在了UI.ViewController命名空间中,而不是UIViewController)。
这个类的Lua类型是table(其实是userdata,但你可以看成是table),因此self.input是Lua表字段而不是OC属性。访问属性你必需用setter/getter方法,例如self.output:setText()。我就是在这一点上被误导了,直到某天我在这个mail list中问到这个问题才恍然大悟,从此再也不会搞混了(这个maillist上的人们都非常友好)。
Wax类也可以实现协议。例如,在这个示例Stats中,演示了用两个定制的UITableviewController类处理UITableView。这个两个类都实现了UITableViewDelegate和UITableViewDataSource协议。列表7展现了一个类,实现了分组表视图的UITableViewDataSource协议。
列表 7: SortedDataSource.lua
waxClass{'SortedDataSource',NS.Object,protocols ={'UITableViewDataSource'},}
function init(self,source_table)
self.source_table = source_table
function numberOfSectionsInTableView(self,tableView)
return#self.source_table.headers
function tableView_numberOfRowsInSection(self,tableView,section)
local index =self.source_table.headers[section+1]
return#self.source_table[index]
function tableView_cellForRowAtIndexPath(self,indexPath)
localidentifier = 'TableViewCell'
local cell = tableView:dequeueReusableCellWithIdentifier(identifier)
cell = cell orUI.TableViewCell:initWithStyle_reuseIdentifier(UITableViewCellStyleDefault,51); font-family:Arial; font-size:14px; line-height:26px"> identifier)
local key =self.source_table.headers[indexPath:section()+1]
localcomponent = self.source_table[key]
local player =component[indexPath:row()+1]
cell:setText(player[1] .. ' ' .. player[2] .. ' ' .. player[3])
return cell
function tableView_titleForHeaderInSection(self,51); font-family:Arial; font-size:14px; line-height:26px"> returnself.source_table.headers[section+1]
注意在tableView_titleForHeaderInSection函数中,Lua字符串和OC字符串是自动转换的。
Wax使用的统一命名方案允许你很容易猜到在Lua中访问OC方法的函数名称。Wax使用TextMate来操作OC调用。例如,你可以直接从Xcode文档粘贴方法签名并转换为Lua调用(我正在犹豫是写一个Emacs函数来干这件事情,还是打开Textmate,然后自己去喝一罐Kool-aid。)
Wax还有许多好东西比如访问sqlite、简单的HTTP请求、XML和JSON处理、CG动画、渐变。另外最近还增加了用Lua编写应用程序委托(不需要在启动Wax时再用OC来实现),从命令行运行测试。最有趣和给力的消息是,Wax现在提供了一个交互式控制台,你可以telnet到模拟器(或者设备上!)与运行的程序进行交互:调整参数或查看运行状态。
Wax是开源的,也使用MIT协议。现在,这个项目由众多开发者参与和使用。
结束语
应用商店中,用Lua开发的app是被接受的。Ansca论坛罗列的Coronaapp的超过了150个主题(我没有全部看过…它们不全部是新的应用)。看过Wax的邮件列表,你会发现鲜有开发者声明在使用Wax编写app,很可能还有更多的开发者没有附在列表中。还有许多App采用了DIY的方法。
Corona提供一种创建iOS应用的可选方案,但前提是你不需要本地UI元素。可以加入一个本地的TextField,但在模拟器中你看不到它们,这个现实让人无语。但考虑到它的Android兼容,Corona仍然值得一看。我喜欢用Corona并希望它能有更多的改善。
DIY的方法是截然不同的做法:你有完全的控制。但如果你需要在Lua和OC间有大量的交互,需要大量的工作去做。Johnson的wax在Lua和OC间桥接得非常好,在LuaC库上也做得很好——有的地方Corona根本就没有进行处理。
尽管强如人意,iOS开发者协议中的近期改变仍然意味着你能在iOS开发中使用Lua而不用担心app被拒绝。我相信在你的iOS项目中可以使用Lua这样的技术能有助于开发。给Corona和Wax一个积极的环境。此外,如果为了方便你更直接地控制对Lua的使用,我建议你充分体会这门语言的精华。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。