ColorTouch class 机制

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
  1. 定义类 PublicCameraViewController
  2. 基类由 base 属性指定 BaseViewController,表示是一个 VC / Activity
  3. properties 设置基类属性 bShowBackBtn 等和自定义属性 isPublicCameraValid
  4. mixins 设置模块类(category)
  5. 定义类初始化接口 init
  6. 定义类的自定义方法 rightBtnEvent

1 类实例的创建和使用

require "PublicCamera.PublicCameraViewController"
local nextVC = PublicCameraViewController.PublicCameraViewController{
    deviceId = self._cellDto.deviceId,
    enterPublicTime = os.date(os.time()),
}
vc:pushViewController(nextVC, true)
  1. 引入 module,并创建实例 nextVC
  2. 打开 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)
  1. 创建 MetaClass table,并且设置 MetaClassProto 为 MetaClass.prototype
  2. 设置 MetaClassProto 为 MetaClass 的 metatable
  3. 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
  1. 创建 newClsnewClsProto 对象,其中 newClsmetatablenewMetaclsProtonewMetaclsProto__call 属性为 base.class.prototype.__call。由此在创建 newCls 的实例时,会调用基类 prototype.__call 方法,进而实现基类的各个属性的初始化

  2. 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
  1. 将 cls 自身和全部基类的 mixins 收集到 allmixins
  2. 将 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 属性
由此构建 PublicCameraViewControllerPopMenuMixin 之间的关系: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
  1. 将初始化变量中的方法,设置到 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
  1. 将类定义中的 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
  1. 调用 PublicCameraViewController.PublicCameraViewController(...) 时,则会查找 newCls 实例 metatable 中的 __call 方法,即 newMetaclsProto.__call 方法,即 base.class.prototype.__call
  2. 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
  1. 将 cls 中的属性值 set 给对象,并触发 setXXX 方法,同时触发 onXXXChanged 方法,部分属性触发 native 方法执行
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容