0 类的定义
module("PublicCameraViewController", package.seeall)
local BaseViewController = require "Base.BaseViewController"
local screenWidth = getApplication():getViewportWidth()
require "PublicCamera.PopMenuMixin"
....
PublicCameraViewController = Class.Class("PublicCameraViewController",
{
base = BaseViewController,
properties={
deviceId = Class.undefined,
model = Class.undefined,
rightImagePath = "app://icon_more.png",
bShowNaviBar = true,
bShowBackBtn = true,
......
--判断摄像头是否失效
isPublicCameraValid = true,
},
},
mixins = {
popMenu = PopMenuMixin,
.....
}
})
function PublicCameraViewController.prototype:init(inlineprops, ...)
BaseViewController.prototype.init(self, inlineprops, ...)
...
self:addEventListener("popped",
function()
self._isDataInited = false
self._groupDisposable.dispose()
end)
self:addEventListener("resumed",function()
self:hideInputMethod(nil)
if not self._isLoaded then
self:showLoading()
end
self:initData()
self:queryForCommentTotalCount()
end)
end
-- override
function PublicCameraViewController.prototype:rightBtnEvent()
self:showPopMenu(self)
end
- 定义类
PublicCameraViewController
- 基类由
base
属性指定BaseViewController
,表示是一个 VC / Activity - 由
properties
设置基类属性bShowBackBtn
等和自定义属性isPublicCameraValid
等 - 由
mixins
设置模块类(category) - 定义类初始化接口
init
- 定义类的自定义方法
rightBtnEvent
1 类实例的创建和使用
require "PublicCamera.PublicCameraViewController"
local nextVC = PublicCameraViewController.PublicCameraViewController{
deviceId = self._cellDto.deviceId,
enterPublicTime = os.date(os.time()),
}
vc:pushViewController(nextVC, true)
- 引入 module,并创建实例 nextVC
- 打开 VC/Activity
2 类机制的简单介绍
2.1 类创建
-- Class.lua
Class = function(name, config)
if name == nil then
name = "Anonymous" .. nameId
nameId = nameId + 1
end
return MetaClass(name, config)
end
可见 Class.Class("PublicCameraViewController", {...})
是一个方法调用
-- Class.lua
MetaClass = {}
MetaClass.className = "MetaClass"
MetaClass.prototype = MetaClassProto
MetaClassProto.constructor = MetaClass
-- MetaClass继承自Object
MetaClass.superclass = Object
setmetatableAndCopyMetaMethods(MetaClassProto, Object.prototype)
-- MetaClass' class is MetaClass
MetaClass.class = MetaClass
setmetatable(MetaClass, MetaClassProto)
- 创建 MetaClass table,并且设置 MetaClassProto 为 MetaClass.prototype
- 设置 MetaClassProto 为 MetaClass 的 metatable
- MetaClassProto.__call 被定义,所以当调用
MetaClass(name, config)
时,会调用到 MetaClass metatable (即 MetaClassProto) 的 __call 方法
MetaClassProto.__call = function(self, name, config)
config = config and config or {}
local base = config.base and config.base or Object
local properties = config.properties and config.properties or {}
local mixins = config.mixins and config.mixins or {}
local statics = config.statics
local methods = config.methods
logClassStyle("create class, base:%s", base:address())
local newCls, newClsProto = createClass(base, name)
logClassStyle("newed class:%s, classproto:%s", newCls:address(), newCls.prototype:address())
newCls.className = name
--process mixins
processMixins(newCls, mixins)
--process properties
processClassProperties(newCls, properties)
--process methods
processMethods(newCls, methods)
--process static methods
processStaticMethods(newCls, statics)
return newCls
end
function createClass(base, clsName)
--construct new class
local newClsProto = {}
local newCls = {}
newClsProto.constructor = newCls
newCls.prototype = newClsProto
newClsProto.__index = newClsProto
--derive from base
newCls.superclass = base
setmetatableAndCopyMetaMethods(newClsProto, base.prototype)
--metaclass
local newMetacls = {}
local newMetaclsProto = {}
newMetacls.className = "Meta" .. clsName
newMetacls.prototype = newMetaclsProto
newMetaclsProto.constructor = newMetacls
newMetaclsProto.__index = newMetaclsProto
--newcls需要构造函数,这在lua中必须设置其metacls protype的__call字段
newMetaclsProto.__call = base.class.prototype.__call
--metaclass is derive from base's metaclass
newMetacls.superclass = base.class
setmetatableAndCopyMetaMethods(newMetaclsProto, base.class.prototype)
--newmetaclass's class is metaclass
newMetacls.class = MetaClass
setmetatable(newMetacls, MetaClass.prototype)
newCls.class = newMetacls
setmetatable(newCls, newMetaclsProto)
return newCls, newClsProto
end
创建
newCls
,newClsProto
对象,其中newCls
的metatable
为newMetaclsProto
,newMetaclsProto
的__call
属性为base.class.prototype.__call
。由此在创建 newCls 的实例时,会调用基类prototype.__call
方法,进而实现基类的各个属性的初始化-
setmetatableAndCopyMetaMethods(newMetaclsProto, base.class.prototype) 设置基类 prototype 为 newMetaclsProto 的 metatable,为此 newCls 继承了基类 base.class.prototype 中的全部属性
- 当取 newCls 的属性 a,在 newCls 中查找,找到则返回
- 找不到,则查找 newMetaclsProto.__index table(也就是 newMetaclsProto 自己),找到则返回
- 找不到,则查找 base.class.prototype.__index 中的属性,根据递归特性,__index 就是 base.class.prototype 自身,即在 base.class.prototype 查找属性
- 一层层查找 super class prototype 中的属性,直到找到
2.2 初始化 mixin
--processMixins(newCls, mixins)
function processMixins(cls, mixins)
--1. collection all mixins
local superCls = cls.superclass
local allmixins = table.shallowcopy(mixins)
while superCls do
if superCls.__mixins then
for k, v in pairs(superCls.__mixins) do
if allmixins[k] == nil then
allmixins[k] = v
end
end
end
superCls = superCls.superclass
end
--2. 将mixins中所有导出的方法平坦到cls.prototype中,superclass的mixins不需要平坦
table.each(allmixins,
function(mixin, name)
local methods = mixin:methods()
table.each(methods,
function(method)
cls.prototype[method] = function(obj, ...)
local mixin = obj.mixins[name]
return mixin[method](mixin, ...)
end
end)
end)
cls.__mixins = allmixins
end
- 将 cls 自身和全部基类的 mixins 收集到 allmixins
- 将 allmixnins 中的方法设置给 cls.prototype
- 即当 cls 调用一个 mixin 方法时,自身找不到,则查找 metatable __index table(即 prototype)中的属性,则查找到前面的 prototype 中设置的 mixin method 属性中指定的方法,即可调用
-- PopMenuMixin.lua
PopMenuMixin = Class.Class("PopMenuMixin",
{
base = Mixin,
properties = {
vc = Class.undefined,
popMenu = Class.undefined,
preVC = Class.undefined,
},
statics = {
methods = function ()
return {"showPopMenu",}
end
}
}
)
function PopMenuMixin.prototype:init(owner)
Mixin.prototype.init(self, owner)
end
--显示菜单
function PopMenuMixin.prototype:showPopMenu(vc)
...
end
如前面设置的 mixin
PopMenuMixin
,则提供给PublicCameraViewController
的方法为showPopMenu
。
-- Mixin.lua
Mixin = Class.Class("Mixin",
{
properties={
owner=Class.undefined,
},
methods={
init=function(self, owner)
self:setOwner(owner)
end
},
statics={
methods=function(self)
return {}
end
}
})
当 PopMenuMixin init 方法调用的时候,将
PublicCameraViewController
设置为PopMenuMixin
的 owner 属性
由此构建PublicCameraViewController
和PopMenuMixin
之间的关系:PublicCameraViewController
可以调用PopMenuMixin
暴露的 methods 方法,PopMenuMixin
可以通过 getOwner() 获取PublicCameraViewController
对象。
2.3 初始化属性(对象创建传入的属性,class 中定义的属性)
-- Class.lua
-- processClassProperties(newCls, properties)
local processClassProperties = function(cls, props)
-- logClassStyle("process properties:%s", table.tostring(props))
local propertyMaps = {}
for k, v in pairs(props) do
propertyMaps[k] = {
propName = k,
realPropName = NameMap[k][1],--"_" .. k,
getterName = NameMap[k][2],--string.getterName(k),
setterName = NameMap[k][3],--string.setterName(k),
changedEvtName = NameMap[k][4],--string.propChangeEvtName(k),
applyName = NameMap[k][5],--string.applyName(k),
initV = v,
needCopy = getmetatable(v) == nil and type(v) == 'table',
}
end
-- 1. 将构建对象时,自定义的属性设置给 props 设置给 cls 实例(self)的 initProperties,同时构建 get 和 set 方法。
-- 2. 将 class 定义时的属性设置给 initProperties,遍历全部的 super class 的属性同样设置给 initProperties
end
2.4 初始化普通方法
-- Class.lua
-- processMethods(newCls, methods)
function processMethods(cls, methods)
local proto = cls.prototype
if methods then
for key, v in pairs(methods) do
proto[key] = v
end
end
end
- 将初始化变量中的方法,设置到 cls.prototype 中
2.5 初始化静态方法
-- Class.lua
-- processStaticMethods(newCls, statics)
function processStaticMethods(cls, methods)
local metacls = cls.class.prototype
if not methods then
return
end
for k, v in pairs(methods) do
metacls[k] = v
end
end
-
将类定义中的 static 属性中定义的方法直接设置到
cls.class.prototype
中,根据前面createClass
方法,其实最终也是设置到newMetaclsProto
中function createClass(base, clsName) ... newMetacls.prototype = newMetaclsProto newMetaclsProto.constructor = newMetacls newMetaclsProto.__index = newMetaclsProto newMetacls.prototype = newMetaclsProto setmetatable(newMetacls, MetaClass.prototype) ... newCls.class = newMetacls ... return newCls, newClsProto end
3 类实例创建
前面讲述了 类定义和创建,那类对象是如何创建的,相关 init 方法等何时被调用呢?
require "PublicCamera.PublicCameraViewController"
local nextVC = PublicCameraViewController.PublicCameraViewController{
deviceId = self._cellDto.deviceId,
enterPublicTime = os.date(os.time()),
}
nextVC 对象是如何被创建的?
查看 createClass 方法:
--Class.lua
function createClass(base,clsName)
--construct new class
...
local newMetaclsProto = {}
...
newMetaclsProto.__index = newMetaclsProto
--newcls需要构造函数,这在lua中必须设置其metacls protype的__call字段
newMetaclsProto.__call = base.class.prototype.__call
...
setmetatableAndCopyMetaMethods(newMetaclsProto, base.class.prototype)
...
setmetatable(newCls, newMetaclsProto)
return newCls, newClsProto
end
- 调用 PublicCameraViewController.PublicCameraViewController(...) 时,则会查找 newCls 实例 metatable 中的 __call 方法,即 newMetaclsProto.__call 方法,即 base.class.prototype.__call
- base 的最终基类是 Object,Object.MetaObject 是 MetaObject,MetaObject 的 prototype 为 MetaObjectProto, MetaObjectProto.__call 方法已经被定义
--All Class's base class
Object = {}
Object.prototype = ObjectProto
ObjectProto.constructor = Object
Object.className = "Object"
--MetaObjectProto
MetaObjectProto = {}
MetaObjectProto.__index = MetaObjectProto
...
--MetaObject
MetaObject = {}
MetaObject.className = "MetaObject"
MetaObject.prototype = MetaObjectProto
MetaObjectProto.constructor = MetaObject
Object.class = MetaObject
setmetatable(Object, MetaObjectProto)
--MetaObject继承自Object
setmetatableAndCopyMetaMethods(MetaObjectProto, ObjectProto)
MetaObject.superclass = Object
--MetametaClass
MetaClassProto = {}
MetaClassProto.__index = MetaClassProto
MetaObjectProto.__call = function(cls, ...)
-- 1. 创建实例
local self = setmetatable({}, cls.prototype)
-- 2. 设置对象的类属性
self.class = cls
-- 3. 将类的 minixs 属性给 对象
initMixins(self, cls)
self.isIniting = yesF
self.isBeforeInit = yesF
-- 4. 如果有自定义初始化方法,则执行自定义方法,若无则执行默认初始化方法 processInitializedProperties
if cls.processInitProperties then
cls.processInitProperties(cls, self)
else
processInitializedProperties(cls, self)
end
self.isBeforeInit = notF
-- 5. 执行 init 方法
if cls.init then
self:init(...)
end
self.isIniting = notF
return self
end
local processInitializedProperties = function(cls, obj)
local initProperties = nil
if not cls.initProperties then return end
for i, propMap in pairs(cls.initProperties) do
local val = propMap.initV
if val ~= undefined then
if propMap.needCopy then
local newVal = {}
for key, v in pairs(val) do
newVal[key] = v
end
doSetter(obj, propMap.setterName, newVal)
--obj[propMap.setterName](obj, newVal)
else
doSetter(obj, propMap.setterName, val)
-- obj[propMap.setterName](obj, val)
end
end
end
end
- 将 cls 中的属性值 set 给对象,并触发 setXXX 方法,同时触发 onXXXChanged 方法,部分属性触发 native 方法执行