-
Notifications
You must be signed in to change notification settings - Fork 115
iOS&OS X集成使用文档(适用1.x.x版本)
- 在Podfile中加入
pod 'LuaScriptCore'
- 在需要使用LuaScriptCore的地方导入头文件
#import "LuaScriptCore.h"
- 下载源码到本地
- 打开Release目录,如果您的是OC项目,可根据需要运行系统选择iOS或者OSX目录;如果您的是Swift项目,则可以根据运行系统选择iOS-Swift或者OSX-Swift目录
- 直接拖动目录到XCode的工程下进行导入,导入时记得勾选"Copy items if needed"。
- 导入成功后则可以正常使用LuaScriptCore了。
本项目提供的库是基于XCode 8.0进行编译的,如果想要要低于XCode 8.0上能够成功编译,需要进行库的重新编译,请根据下面的步骤实现:
- 打开Source/iOS_OSX/目录中的LuaScriptCore项目文件。
- 选择你想要重新编译的平台,如果是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项目,在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项目,则在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项目,在Objective-C代码中通过context调用Lua方法
LSCValue *value = [self.context callMethodWithName:@"add"
arguments:@[[LSCValue integerValue:1000],
[LSCValue integerValue:24]]];
NSLog(@"result = %@", [value toNumber]);
如果是Swift项目,在Swift代码中通过context调用Lua方法
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)所定义的方法都是全局的,有时候需要对一系列的方法进行一个包装,希望这些方法都归类在某个模块下,一来方便根据模块来划分方法的功能,二来可以避免名称的冲突。因此,可以使用LSCModule来对Lua的功能进行扩展。需要根据下面步骤实现:
- 新声明一个模块类继承于LSCModule类(必须继承于LSCModule类,否则无法进行模块注册)
- 在创建的模块类中定义需要导出到lua的类方法(即+号方法,注:-号方法是不会进行导出),声明方法完全跟Objective-C/Swift的方法一样(对于不想导出到Lua的方法可以在定义是使用下划线“_”开头),但需要注意的是结构体是无法作为参数或者返回值与Lua进行交互。
- 调用LSContext的registerModuleWithClass方法(Swift中调用LuaContext的registerModule方法)将定义的类型传入即完成功能模块的扩展。
下面例子定义了一个日志模块,如:
在Objective-C代码中定义LSCModule的子类
/**
* 日志模块
*/
@interface LogModule : LSCModule
/**
* 写入日志
*
* @param message 日志信息
*/
+ (void)writeLog:(NSString *)message;
@end
@implementation LogModule
+ (void)writeLog:(NSString *)message
{
NSLog(@"** message = %@", message);
}
@end
注册模块
[self.context registerModuleWithClass:[LogModule class]];
如果你的是Swift项目,则可以在Swift代码中进行如下定义
class LogModule: LSCModule
{
static func writeLog(message : String) -> Void
{
NSLog("** message = %@", message);
}
}
然后注册模块
_context.registerModule(moduleClass: LogModule.self);
在Lua代码中调用
LogModule.writeLog('Hello Lua Module!');
Lua中没有明确的面向对象特性,LuaScriptCore提供了一个面向对象的扩展,Lua可以简单地封装类型,并且进行类型实例的使用。首先需要先注册面向对象的模块类LSCObjectClass(如果原生层有继承于LSCObjectClass类的子类需要注册时,该步骤可省略),如:
Objective-C中
[self.context registerModuleWithClass:[LSCObjectClass class]];
Swift中
_context.registerModule(moduleClass: LSCObjectClass.self);
注册成功后,可以直接在Lua中定义类型,如:
Object.subclass("MyClass");
-- 重写类型的初始化方法
function MyClass:init ()
print("MyClass instance initialize...");
end
-- 定义类型属性
MyClass.property = 0;
local instance = MyClass.create();
print (instance.property);
上述代码的subclass就是子类化对象方法,其接收一个类型名称参数,调用后会创建一个对应名称的类型。该类型拥有Object的所有特性。
对于Lua的类定义有如下设定:
- 所有类型的根类是Object,这个注册LSCObjectClass后会有这个类的定义.
- 必须使用subclass方法来进行子类化,必须传入一个有效并且不重复的类型名称参数。
- 必须使用类型的create方法进行类对象的实例化。
- 类型中的方法(即类方法)都以.号进行调用,类实例中的方法都以:号进行调用。类型和实例的属性都以.号进行访问。
- 每个类中都有一个叫super的属性来指代其父类型。
- 需要调用父类方法时,请使用"类型.super.method"方式进行调用,如:
MyClass.super.test();
。- 当类实例对象初始化时会触发init方法,销毁时会触发destroy方法,可以通过重写这些方法来进行一些额外的工作。
如果需要让原生类直接映射到Lua中,按照下面步骤实现:
- 定义一个新的类继承于LSCObjectClass类。
- 定义新类型的属性和方法(注:原生类中的属性会转换为Lua类中的setXXX和XXX方法),对于不想导出到Lua中的方法或属性可以在声明时使用下划线“_”开头。
- 定义类的类方法会导出给类型,定义实例方法会导出给类型的实例。
- 调用LSContext的registerModuleWithClass方法(Swift中调用LuaContext的registerModule方法)将定义的类型传入即完成Lua类的声明。导入时可以直接导入子类,LSContext(或LuaContext)会检测是否有导入根类型。
如在OC中有如下类型定义
@interface LSCTPerson : LSCObjectClass
@property (nonatomic, copy) NSString *name;
+ (NSString *)moduleName;
+ (void)printPersonName:(LSCTPerson *)person;
- (void)speak;
- (void)walk;
@end
@implementation LSCTPerson
+ (NSString *)moduleName
{
//变更导出的类型名称
return @"Person";
}
+ (void)printPersonName:(LSCTPerson *)person
{
NSLog(@"Person name = %@", person.name);
}
- (void)speak
{
NSLog(@"%@ speak", self.name);
}
- (void)walk
{
NSLog(@"%@ walk", self.name);
}
@end
然后对该类型进行注册:
[self.context registerModuleWithClass:[LSCTPerson class]];
在Swift中可以如下定义
class LSCTPerson: LSCObjectClass
{
static override func moduleName() -> String
{
return "Person";
}
static func printPersonName(p : LSCTPerson) -> Void
{
NSLog("%@", p.name);
}
var name : String? = nil;
func speak() -> Void
{
NSLog("%@ speak", name ?? "noname");
}
func walk() -> Void
{
NSLog("%@ walk", name ?? "noname");
}
}
然后对该类型进行注册:
_context.registerModule(moduleClass: LSCTPerson.self);
注册成功后,在lua中即可使用该类型:
local person = Person.create();
person:setName("vimfung");
person:walk();
person:speak();
Person.printPersonName(person);
从v1.3.1版本开始,LuaScriptCore提供了ClassImport允许动态导入原生类型到Lua的功能。目的是为了帮助在程序执行过程中需要调用非LSCObjectClass子类型的原生对象功能。若要使用ClassImport请注册模块:
Objective-C中
[self.context registerModuleWithClass:[LSCClassImport class]];
Swift中
_context.registerModule(moduleClass: LuaClassImport.self);
假设存在一个LSCTNativeData的类型,如:
Objective-C中
@interface LSCTNativeData : NSObject
@property (nonatomic, copy) NSString *dataId;
+ (LSCTNativeData *)createData;
- (void)setData:(NSString *)data key:(NSString *)key;
- (NSString *)getData:(NSString *)key;
@end
Swift中
class LSCTNativeData: NSObject
{
var dataId : String? = nil;
private var _data : Dictionary<String, String> = Dictionary();
public class func createData () -> LSCTNativeData
{
return LSCTNativeData();
}
public func setData(value : String, key : String) -> Void
{
_data[key] = value;
}
public func getData(key : String) -> String
{
return _data[key]!;
}
}
然后设置允许动态导出的类型,基于安全的考虑默认是不允许导出任意类型到Lua中的,所以必须要设置哪些类型允许被导出,如:
Objective-C中
[LSCClassImport setInculdesClasses:@[LSCTNativeData.class] withContext:self.context];
Swift中
LuaClassImport.setInculdesClasses(classes: [LSCTNativeData.self], context: _context);
然后在Lua中进行使用:
local Data = ClassImport('LSCTNativeData');
-- 如果为Swift类型需要带模块名称,如:local Data = ClassImport('ModuleName.LSCTNativeData');
print(Data);
local d = Data.create();
print(d);
d:setDataId('xxxx');
print(d:dataId());
d:setData('xxx','testKey');
print(d:getData('testKey'));
关于ClassImport的几点注意事项
- 只能导入允许的类型。
- 当导入LSCObjectClass的子类型时会触发类型注册,并返回指定类型。
- ClassImport导入的类型为全属性全方法导出,不带有继承特性(导入LSCObjectClass子类型除外)。