-
Notifications
You must be signed in to change notification settings - Fork 115
iOS&OS X集成使用文档
- 在Podfile中加入
pod 'LuaScriptCore'
- 在需要使用LuaScriptCore的地方导入头文件
#import "LuaScriptCore.h"
- 下载LuaScriptCore_iOS_OSX_2.3.2.zip
- 解压压缩包
- 直接拖动目录到XCode的工程下进行导入,导入时记得勾选"Copy items if needed"。
- 导入成功后则可以正常使用LuaScriptCore了。
本项目提供的库是基于XCode 9.0进行编译的,如果想要要低于XCode 9.0上能够成功编译,需要进行库的重新编译,请根据下面的步骤实现:
- 打开Source/iOS_OSX/目录中的LuaScriptCore项目文件(如果你需要Lua5.1核心的库则需要打开LuaScriptCore_5_1项目文件)
- 选择你想要重新编译的平台,如果是iOS的OC版本则选择LuaScriptCore-iOS-output的scheme,Swift版本则选择LuaScriptCore-iOS-Swift-output的scheme;如果是OS X的OC版本则选择LuaScriptCore-OSX-output的scheme,Swift版本则选择LuaScriptCore-OSX-Swift-output的scheme。
- Command + B 进行重新编译,编译后的库文件和头文件会放到Release目录下替换原有的文件。
LuaScriptCore中提供了LSCContext(Swift中为LuaContext)这个上下文对象类来维护整个Lua的环境,所以使用LuaScriptCore的第一步就是对Context进行初始化。如:
Objective-C
LSCContext *context = [[LSCContext alloc] init];
Swift
var _context : LuaContext = LuaContext();
初始化完毕后才能进行下面的各种操作。
原生层中要操作Lua中的变量或者方法,通常使用到的就是组织Lua脚本来进行一些列的Lua操作(LuaScriptCore屏蔽了Lua库中的C Api,目的是让代码更加接近原生,使用起来更加得心应手),然后交由Context进行脚本的解析。如:
Objective-C
[self.context evalScriptFromString:@"print('Hello World');"];
Swift
_context.evalScript(script: "print('Hello World');");
对于一些耗时或者Lua无法实现的功能(如:手机设备的拍照),都可以考虑交由原生层来实现,然后提供接口让Lua进行调用。如下面获取手机设备信息的方法:
Objective-C
[self.context registerMethodWithName:@"getDeviceInfo" block:^LSCValue *(NSArray *arguments) {
NSMutableDictionary *info = [NSMutableDictionary dictionary];
[info setObject:[UIDevice currentDevice].name forKey:@"deviceName"];
[info setObject:[UIDevice currentDevice].model forKey:@"deviceModel"];
[info setObject:[UIDevice currentDevice].systemName forKey:@"systemName"];
[info setObject:[UIDevice currentDevice].systemVersion forKey:@"systemVersion"];
return [LSCValue dictionaryValue:info];
}];
Swifit
_context.registerMethod(methodName: "getDeviceInfo", block: { (arguments : Array<LuaValue>) -> LuaValue in
var info : Dictionary<String, String> = Dictionary<String, String>();
info["deviceName"] = UIDevice.current.name;
info["deviceModel"] = UIDevice.current.model;
info["systemName"] = UIDevice.current.systemName;
info["systemVersion"] = UIDevice.current.systemVersion;
return LuaValue(dictionaryValue: info);
});
在Lua代码中即可调用此方法
local tbl = getDeviceInfo();
有时候需要通过调用Lua中的方法来取得返回值,然后再做后续的处理,如在Lua代码中有add方法的定义:
function add (a, b)
return a+b;
end
原生代码调用如下:
Objective-C
LSCValue *value = [self.context callMethodWithName:@"add"
arguments:@[[LSCValue integerValue:1000],
[LSCValue integerValue:24]]];
NSLog(@"result = %@", [value toNumber]);
Swift
let retValue : LuaValue? = _context.callMethod(methodName: "add",
arguments: [LuaValue(intValue: 1000), LuaValue(intValue:24)]);
if (retValue != nil)
{
NSLog("result = %d", retValue!.intValue);
}
LSCContext(Swift中为LuaContext)所定义的方法都是全局的,有时候需要对一系列的方法进行一个封装,希望这些方法都归类在某个模块下,一来方便根据模块来划分方法的功能,二来可以避免名称的冲突,同时也可以使用面向对象的方式在Lua中进行代码编写。因此,LuaScripCore提供了LSCExportType
(Swift中为LuaExportType
)来让原生代码中的类型方便快速地导入到Lua中,供Lua进行调用。其实现步骤如下:
- 新声明一个原生类型并实现LSCExportType(Swifth中为LuaExportType)协议
- 在创建的类型中定义需要导出到Lua的方法和属性,声明方法完全跟Objective-C/Swift的方法一样(对于不想导出到Lua的方法可以在定义时使用下划线“_”开头),需要注意的是结构体是无法作为参数或者返回值与Lua进行交互。
下面例子定义了一个日志模块,如:
Objective-C
/**
* 日志模块
*/
@interface Log : NSObject <LSCExportType>
/**
* 前缀
*/
@property (nonatomic, copy) NSString *prefix;
/**
* 写入日志
*
* @param message 日志信息
* @param prefix 前缀
*/
- (void)writeLog:(NSString *)message;
/**
* 共享实例
* /
+ (Log *)sharedInstance;
@end
@implementation LogModule
- (void)writeLog:(NSString *)message
{
NSLog(@"** [%@] message = %@", self.prefix, message);
}
+ (Log *)sharedInstance
{
static Log *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[Log alloc] init];
});
return instance;
}
@end
Swift
class Log : NSObject, LuaExportType
{
var prefix : String = "";
func writeLog(message : String) -> Void
{
NSLog("** [%@] message = %@", prefix, message);
}
private Log _instance = Log();
static func sharedInstance() -> Log
{
return _instance;
}
}
注:Swift4后对于导出方法请在方法声明中加入@objc, 如:
@objc(LogModule)
class LogModule : NSObject, LuaExportType
{
@objc var prefix : String = "";
@objc func writeLog(message : String) -> Void
{
NSLog("** [%@] message = %@", prefix, message);
}
private Log _instance = Log();
@objc static func sharedInstance() -> Log
{
return _instance;
}
}
在Lua中则可进行如下调用:
local log = Log:sharedInstance();
log.prefix = "LuaScriptCore";
log:writeLog('Hello Lua Module!');
导出到Lua的原生类型可以直接通过类型()
方式来构造类型对象。如:
local obj = Object();
如果需要在实例对象构造或者销毁时做一些操作,则可以通过重写类型的init
和destroy
方法来实现。如:
function Object.prototype:init ()
print ("object created!");
end
function Object.prototype:destroy()
print ("object destroy!");
end
Lua中引入了原型prototype
实现,每个导出的原生类型带有此属性。其包含了类型的实例方法和属性定义。如果需要扩展某个类型的实例方法和属性,则可通过修改prototype
来实现。例如为Object
类型增加print
实例方法:
function Object.prototyp:print()
print(self);
end
对于类方法扩展,则可以直接在类型中进行声明,如:
function Object:createObject(name)
local instance = self();
instance.name = name;
return instance;
end
Lua中可以通过subclass
直接子类化原生类型来生成一个新的类型,如:
Object:subclass("MyClass");
-- 重写类型的初始化方法
function MyClass:init ()
print("MyClass instance initialize...");
end
-- 定义类型属性
MyClass.prototype.propertyName = 0;
local instance = MyClass();
print (instance.propertyName);
上述代码的subclass就是子类化对象方法,其接收一个类型名称参数,调用后会创建一个对应名称的类型。该类型拥有Object的所有特性。 当继承类需要覆盖父级类型的某个方法时,只要直接重写一遍父级方法即可,如下代码所示:
-- 创建一个Object的类方法
function Object:classMethodName ()
print ("Object classMethodName call");
end
-- 创建子类
Object:subclass("MyClass");
-- 覆盖父类方法
function MyClass:classMethodName ()
print ("MyClass classMethodName call");
end
如果需要在覆盖方法中调用父级方法,可以使用类型的super
属性,如:
function MyClass:classMethodName ()
MyClass.super.classMethodName(self);
print ("MyClass classMethodName call");
end
实例方法的覆盖方式大致相同,如:
-- 创建一个Object的实例方法
function Object.prototype:instanceMethodName ()
print ("Object instanceMethodName call");
end
-- 创建子类
Object:subclass("MyClass");
-- 覆盖父类方法
function MyClass.prototype:instanceMethodName ()
-- 调用父级方法
MyClass.super.prototype.instanceMethodName(self);
print ("MyClass instanceMethodName call");
end