動態語言 & ios程序 - lua基礎


經常有看到一些游戲可以不通過appstore而修改一些東西,比如增加功能。

這個其實就是通過下載腳本來實現的。常見的腳本就是js和lua吧。

個人對lua比較熟悉。

lua這貨還是挺牛的,解釋器非常小,速度也非常快,和C語言函數的交互也很容易。

集成lua到xcode工程

很容易,

1. 到lua官網,下載源代碼,http://www.lua.org/download.html

2. 在要集成lua的工程里面,添加一個target,選擇靜態庫。

3. 把lua源代碼(.h, .c)拉到新建的靜態庫里面,編譯。

4. 在目標工程里面,link剛編譯出來的.a文件。如:


這樣環境就算弄好了。


使用lua

先include幾個lua的頭文件。

//extern "C"
//{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
//}
如果是c++環境,則需要把extern "C"打開。


准備Lua腳本文件

這里,就簡單的創建一個資源。實際上這個腳步可以從服務器下載,保存在本地機器。

創建本地資源文件很簡單,我這里就直接創建一個空的文件,命名為my.lua。


然后寫入腳本:


這個腳本非常的簡單,就是定義一個全局變量myname和一個函數lefthandcall。

在函數內部調用一個lhc的函數,返回。


調用lua函數

直接給出代碼,看注釋:

- (IBAction)testLua:(id)sender {
    l = luaL_newstate();  // 新建一個lua狀態
    
    luaL_openlibs(l);  // 加載lua庫
    
    NSString *scriptpath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"my.lua"];  // 讀取資源文件里面的lua文件路徑
    //    NSString* content = [NSString stringWithContentsOfFile:scriptpath encoding:NSUTF8StringEncoding error: nil];
    int iError = luaL_loadfile(l, scriptpath.UTF8String);  // 加載lua文件
    iError = lua_pcall(l, 0, 0, 0);  // 先執行一下,可以獲取lua腳本的一些變量
    
    int iTop = lua_gettop(l);  // 獲取棧頂元素的索引,這里應該是0,也就是棧還是空的。
    
    lua_getglobal(l, "myname");  // 獲取lua里面的一個名字為myname的全局變量,這個全局變量的值會壓棧
    
    iTop = lua_gettop(l);  // 所以這里應該是1, 棧頂索引時1,也就是只有一個元素。
    
    const char* p = lua_tostring(l, -1);  // 可以獲取lua腳本里面的myname變量的值。
    printf("global variable value: %s", p);
    
    
    lua_pushcfunction(l, myTest);  // 把一個c語言函數壓棧。
    
    iTop = lua_gettop(l);  // 現在應該是2
    
    lua_setglobal(l, "lhc");  // 把棧頂的myTest函數設置成lua環境里面的全局變量,名字為lhc,lua_setglobal函數有出棧功能。
    
    iTop = lua_gettop(l);  // 這個時候,應該是1,這個元素其實就是myname變量的值,可以再次嘗試獲取。
    
    p = lua_tostring(l, -1);
    
    lua_pop(l, 1);  // 從棧頂移除一個元素。
    
    iTop = lua_gettop(l);  // 應該是0
    
    lua_getglobal(l, "lefthandcall");  // 獲取lua腳本里面的名字為lefthandcall的函數,壓棧。
    iTop = lua_gettop(l);  // 應該是1
    lua_pushnumber(l, 15);  // 壓入參數
    lua_pushnumber(l, 20);  // 壓入參數
    
    iTop = lua_gettop(l);  // 棧頂索引是3,一個lua腳本的函數,2個參數。
    
    iError = lua_pcall(l, 2, 1, 0 );  // 調用lua函數,會把棧里面的lua函數和兩個參數移除,然后把返回值壓棧。
    
    iTop = lua_gettop(l);  // 3 - 3 + 1 = 1
    
    
    printf("ret: %s", lua_tostring(l, -1));  // 打印返回值。
    
    lua_pop(l, iTop);  // 清空棧。
    
    iTop = lua_gettop(l);  // 0
    
    lua_close(l);  // 關閉lua狀態。
}
上面的代碼主要做了這么幾個事情:

1. 新建一個lua狀態

2. 獲取一個lua腳本里的全局變量。

3. 把一個C語言函數myTest壓棧,並且設置成lua環境里面的一個全局函數。

4. 獲取lua腳本的函數,並且壓入相應的參數

5. 調用lua腳本函數。

mytest函數代碼如下:

int myTest(lua_State* L)  // 這個函數會被腳本執行。
{
    // 先檢測傳入的2個參數是否為數值型
    if (!lua_isnumber(L, 1)){
        return lua_error(L);
    }
    if (!lua_isnumber(L, 2)){
        return lua_error(L);
    }
    
    double a = lua_tonumber(L, 1);
    double b = lua_tonumber(L, 2);
    a>b?lua_pushnumber(L, a):lua_pushnumber(L, b);  // 把2個參數里面大的值壓棧,也就是返回值。
    

    root.myLabel.hidden = NO;  // 顯示一個原來是隱藏的label。
    
    return 1;
}
基本上,整個過程就是:

1. iOS的按鈕響應函數調用lua腳本函數lefthandcall

2. lua腳本函數lefthandcall調用lhc函數

3. lhc是lua環境里的一個全局函數,由#1設置。lhc的本身代碼是C語言。也就是上面的myTest函數。

運行一下,就會得到2個結果:

1. myTest函數的返回值,也就是2個參入參數的大的值

2. myTest里面,把一個label給顯示出來了。見下圖,點擊TestLua按鈕上,會顯示上面的label “Shown by Lua”




總結

在ios開發里面使用lua還是挺簡單的,也很好用。上面的例子,我們顯示了一個label,實際上可以做更多其他的事情。

比如,擴展一些簡單的功能。

有了lua這種動態執行腳本,可以給我們的程序增加一些靈活性。有些時候,做一些功能的改動還是挺好的,可以省去再次審核,但是有時像appstore可能會因為集成了這類功能,而拒絕審核。但是不管怎么樣,這總是給了我們一個比較好的小范圍調整程序的方法。

當然,如果改動很大,那最好的辦法還是發布一個新的app版本,不然lua會整的非常復雜。


附:

xcode 6, iOS sdk 8, lua 5.3

//
//  ViewController.m
//  TestLua
//
//  Created by Kevin on 15/3/3.
//  Copyright (c) 2015年 Kevin. All rights reserved.
//

#import "ViewController.h"

//extern "C"
//{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
//}

lua_State *l;
ViewController* root;

int myTest(lua_State* L)  // 這個函數會被腳本執行。
{
    // 先檢測傳入的2個參數是否為數值型
    if (!lua_isnumber(L, 1)){
        return lua_error(L);
    }
    if (!lua_isnumber(L, 2)){
        return lua_error(L);
    }
    
    double a = lua_tonumber(L, 1);
    double b = lua_tonumber(L, 2);
    a>b?lua_pushnumber(L, a):lua_pushnumber(L, b);  // 把2個參數里面大的值壓棧,也就是返回值。
    

    root.myLabel.hidden = NO;  // 顯示一個原來是隱藏的label。
    
    return 1;
}

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Do any additional setup after loading the view, typically from a nib.
    
    root = self;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    

}

- (IBAction)testLua:(id)sender {
    l = luaL_newstate();  // 新建一個lua狀態
    
    luaL_openlibs(l);  // 加載lua庫
    
    NSString *scriptpath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"my.lua"];  // 讀取資源文件里面的lua文件路徑
    //    NSString* content = [NSString stringWithContentsOfFile:scriptpath encoding:NSUTF8StringEncoding error: nil];
    int iError = luaL_loadfile(l, scriptpath.UTF8String);  // 加載lua文件
    iError = lua_pcall(l, 0, 0, 0);  // 先執行一下,可以獲取lua腳本的一些變量
    
    int iTop = lua_gettop(l);  // 獲取棧頂元素的索引,這里應該是0,也就是棧還是空的。
    
    lua_getglobal(l, "myname");  // 獲取lua里面的一個名字為myname的全局變量,這個全局變量的值會壓棧
    
    iTop = lua_gettop(l);  // 所以這里應該是1, 棧頂索引時1,也就是只有一個元素。
    
    const char* p = lua_tostring(l, -1);  // 可以獲取lua腳本里面的myname變量的值。
    printf("global variable value: %s", p);
    
    
    lua_pushcfunction(l, myTest);  // 把一個c語言函數壓棧。
    
    iTop = lua_gettop(l);  // 現在應該是2
    
    lua_setglobal(l, "lhc");  // 把棧頂的myTest函數設置成lua環境里面的全局變量,名字為lhc,lua_setglobal函數有出棧功能。
    
    iTop = lua_gettop(l);  // 這個時候,應該是1,這個元素其實就是myname變量的值,可以再次嘗試獲取。
    
    p = lua_tostring(l, -1);
    
    lua_pop(l, 1);  // 從棧頂移除一個元素。
    
    iTop = lua_gettop(l);  // 應該是0
    
    lua_getglobal(l, "lefthandcall");  // 獲取lua腳本里面的名字為lefthandcall的函數,壓棧。
    iTop = lua_gettop(l);  // 應該是1
    lua_pushnumber(l, 15);  // 壓入參數
    lua_pushnumber(l, 20);  // 壓入參數
    
    iTop = lua_gettop(l);  // 棧頂索引是3,一個lua腳本的函數,2個參數。
    
    iError = lua_pcall(l, 2, 1, 0 );  // 調用lua函數,會把棧里面的lua函數和兩個參數移除,然后把返回值壓棧。
    
    iTop = lua_gettop(l);  // 3 - 3 + 1 = 1
    
    
    printf("ret: %s", lua_tostring(l, -1));  // 打印返回值。
    
    lua_pop(l, iTop);  // 清空棧。
    
    iTop = lua_gettop(l);  // 0
    
    lua_close(l);  // 關閉lua狀態。
}
@end














注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com