你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> 用Lua編寫iOS程序

用Lua編寫iOS程序

編輯:IOS開發綜合

本文討論用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,32.4, 34.0}

   print(car_data[1])               -- 30

 print(car_data['license'])       -- XVW1942

print(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

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

end

 

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 是一個app的全部源代碼。

 

列表3: Swirly Text 應用中的main.lua

 

local w, h = display.stageWidth, display.stageHeight

local dx, dy, dtheta = 5, 5, 5

 

 

local background = display.newRect(0, 0, w, h)

background:setFillColor(255, 255, 255)

 

 

local message = display.newText('Hello from Corona', w/2, h/2)

message:setTextColor(0, 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

     counter_spin = true

   end

   if counter_spin then

     dtheta = -1 * dtheta

   end

end

 

 

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

end

 

把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公司的網站暗示游戲版的價格只是預覽版的。這意味著當正式版發布時價格將會更高。

 

DIY

 

很容易把Lua解釋器放到iOS app中。打開一個Xcode項目把Lua的源文件(除lua.c和luac.c命令行程序外)加到項目中。編譯。你就可以使用標准的LuaC API去創建一個解釋起並運行源代碼,就像 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(L, [input.text

                    cStringUsingEncoding:NSASCIIStringEncoding]);

    if (0 != err) {

        output.text = [NSString stringWithCString:lua_tostring(L, -1)

                            encoding:NSASCIIStringEncoding];

        lua_pop(L, 1);

        return;

    }

 

    err = lua_pcall(L, 0, LUA_MULTRET, 0);

    if (0 != err) {

        output.text = [NSString stringWithCString:lua_tostring(L, -1)

                            encoding:NSASCIIStringEncoding];

        lua_pop(L, 1);

        return;

    }

    int nresults =lua_gettop(L);

 

    if (0 == nresults) {

        output.text = @"<no results>";

    } else {

        NSString *outputNS = [NSString string];

        for(int i = nresults; i > 0; i--) {

           outputNS = [outputNSstringByAppendingFormat:@"%s ",

                                              lua_tostring(L, -1* i)];

        }

        lua_pop(L, nresults);

        output.text = outputNS;

    }

}

 

注意錯誤的捕捉和處理被極度簡化了。一個好的Luashell應該要做得更多一些,並不是隨隨便便就可以往蘋果商店中放的…

這樣做的弊端在哪裡?最大的問題是到OC的橋接丟失了。理想狀態下我們應該從Lua中調用OC方法,以及相反方向的調用。Lua中要是能實現代碼回調和委托方法就太爽了。

 

iPhone Wax

 

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, 280, 225))

 

    localevalButton = UI.Button:buttonWithType(UIButtonTypeRoundedRect)

   evalButton:setTitle_forState('Evaluate', UIControlStateNormal)

    evalButton:setFrame(CGRect(200,142, 100, 32))

   evalButton:addTarget_action_forControlEvents(self, 'eval:',

                           UIControlEventTouchUpInside)

   self.evalButton = evalButton

   

   self:view():addSubview(self.input)

    self:view():addSubview(self.output)

   self:view():addSubview(self.evalButton)

   

    return self

end

 

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

   

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

    return self

end

 

 

function numberOfSectionsInTableView(self, tableView)

    return#self.source_table.headers

end

 

 

function tableView_numberOfRowsInSection(self, tableView, section)

    local index =self.source_table.headers[section+1]

    return#self.source_table[index]

end

 

 

function tableView_cellForRowAtIndexPath(self, tableView,indexPath) 

    localidentifier = 'TableViewCell'

    local cell = tableView:dequeueReusableCellWithIdentifier(identifier)

    cell = cell orUI.TableViewCell:initWithStyle_reuseIdentifier(UITableViewCellStyleDefault,

                                                     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

end

 

function tableView_titleForHeaderInSection(self, tableView, section)

    returnself.source_table.headers[section+1]

end

 

注意在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的使用,我建議你充分體會這門語言的精華。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved