基于lua的观察者模式应用:事件分发系统

这篇博客探讨了事件系统在处理回调函数时的关键点,包括事件订阅的优先级、派发过程中的事件处理以及如何在派发过程中安全地取消订阅。文章详细介绍了事件注册、排序、取消以及派发的实现,特别强调了在事件派发过程中不能进行优先级排序以防止回调重复触发的问题。同时,还展示了如何标记并清理在派发过程中需要删除的监听器。
摘要由CSDN通过智能技术生成
local b = class()
function B:on_money_change(money)
	print(money,"B recive event")
end
EventSystem:on(Event.MoneyChanged,B.on_money_change,{target = B})
--定义回调函数和注册事件
local c = class()
function C:on_money_change(money)
	print(money,"C recive event")
end
EventSystem:on(Event.MoneyChanged,C.on_money_change,{target = C})

EventSystem:emit(Event.MoneyChanged,10)

关键点:

  1. 在事件派发的过程中订阅该事件,订阅还有优先级,需要小心处理排序问题
  2. 在事件派发的过程中取消订阅该事件,需要采用标记移除,不能直接移除
  3. 在事件派发的过程中又派发了该事件,如何确定事件派发完成
--注册接口实现
function EventSystem:on( event, func, params )
    --- ...
    local event_listener = self._listeners[event]
    params = params or {}
    local priority = params.priority or 0 --事件优先级设定
    local target = params.target
    --- ...
    local cb = {target = target, func = func, id = id, priority = priority}
    table.insert(event_listener.list, cb)
    id = id + 1
    if priority > 0 then
        event_listener.need_sort = true
        self:sort(event_listener)
    end
end
--排序事件
function EventSystem:sort( listener )
    if listener.need_sort == true and listener.emit_count == 0 then
    -- listener.emit_count == 0 当前事件没有处于派发状态
    --[[事件处于派发状态时不能进行优先级排序原因是可能会造成回调的重复触发。
       比如当前事件有4个回调 a, b, c, d,派发事件是顺序执行回调,当执行到第3个回调c时,如果在c回调中                  又注册了一个优先级最高的回调e,立刻排序的话,e插入到第一位,c会被挤到第4位,顺序执行到第4个回调时,导致c又被调用一次
    ]]
        table.sort(listener.list, function ( a, b )
            if a.priority == b.priority then
                return a.id < b.id
            else
                return a.priority > b.priority
            end
        end)
        listener.need_sort = false;
    end
end
--监听取消函数
function EventSystem:off( event, func, params )
    --- ...
    local event_listener = self._listeners[event]
    params = params or {}
    for i,cb in ipairs(event_listener.list) do
        if cb.func == func and cb.target == params.target then
            if event_listener.emit_count > 0 then
                -- 派发过程中只进行标记删除
                cb.need_remove = true
                event_listener.need_clean = true
            else
                table.remove(event_listener.list, i)
            end
            break;
        end
    end
end
--事件派发接口
function EventSystem:emit( event, ... )
    --- ...
    local event_listener = self._listeners[event]
    local interrupt = false
    local length = #event_listener.list
    -- 这里不能使用ipairs,确保不会触发在派发过程中注册的事件
    -- 只取当前已经注册的事件数量,如果在派发过程中再注册(调用了table.insert),本次派发也不会调用
    for i = 1, length do
        if interrupt == true then
            break
        end
        local cb = event_listener.list[i]
        if cb.func and cb.need_remove ~= true then
            event_listener.emit_count = event_listener.emit_count + 1
            if cb.target then
                interrupt = cb.func(cb.target, ...)
            else
                interrupt = cb.func(...)
            end
            event_listener.emit_count = event_listener.emit_count - 1
        end
    end
    self:sort(event_listener);
    self:clean(event_listener);
    return interrupt
end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值