iOS Swift 中的变量侦听器

Variable listener in iOS Swift

我正在尝试制作一个独立的 class/library,它 运行 可以自行处理内部错误,但也会向前端提供反馈(状态)以进行可能的用户交互。在 Android 中,我将使用 Listener 解决此问题:

    class Status(errCode:Int=0, msg:String=""){
    var errCode:Int=0
    var text:String=""
    private var listener: ChangeListener? = null

    init {
        this.errCode=errCode
        this.text=msg
    }

    fun set(errCode:Int, msg:String) {
        this.errCode=errCode
        this.text=msg
        if (listener != null) listener!!.onChange()
    }

    fun getListener(): ChangeListener? {
        return listener
    }

    fun setListener(listener: ChangeListener?) {
        this.listener = listener
    }

    interface ChangeListener {
        fun onChange()
    }
}

每当我想更新它时,例如在异常情况下,我调用:

catch (e: IOException) {
            this.status.set(109,"someException: $e")
        }

在 MainActivity 中,我只需要处理这些更改:

myObj.status.setListener(object : Status.ChangeListener {
            override fun onChange() {
                when(myObj!!.status.errCode) {
                    //errors
                    109 -> log(myObj!!.status.text) //some Exception
                    111 -> myObj!!.restart() //another exc
                    112 -> myObj!!.checkAll() //some other error
...

在Swift中我发现didSet看起来很相似。我读到你可以将它附加到一个结构上,这样每当结构中的某些内容发生变化时,它就会被调用。所以我将我的“struct Status”添加到我的“struct myObj”中,如下所示:

struct myObj {
var status:Status=Status(errCode: 0, msg: "Init")

struct Status {
    var errCode:Int
    var msg:String
    
    mutating func set(err:Int, txt:String){
        errCode=err
        msg=txt
    }
}

并且在我的代码中我初始化了一个新的 myObj 实例

var mObj:myObj=myObj.init() {
        didSet{
            switch myObj.status.errCode{
            case 1:
                promptOkay()
            case 113:
                obj.errorHandling()
            default:
                log(txt: mObj.status.msg)
            }
        }
    }

然而,它永远不会触发 didSet,即使内部函数应该改变 Status。我读到 didSet 没有在 init 上触发,但是在初始化应该 运行 完全独立的 class 对象后,我现在没有 运行 任何东西。我想先验证一下这个方法是否可行,然后再进一步,然后再解开所有的问题。

didSet 必须在 属性 上声明,而不是在初始化期间声明:

class MyObj {
    var status: Status = Status(errCode: 0, msg: "Init") {
        didSet {
            // status did change
            print("new error code: \(status.errCode)")
        }
    }
    
    struct Status {
        var errCode:Int
        var msg:String
        
        mutating func set(err:Int, txt:String){
            errCode = err
            msg = txt
        }
    }
}

let obj = MyObj()
obj.status.set(err: 10, txt: "error 10") // prints "new error code: 10"

此时您可以对 didSetClosure 中对 obj.status 所做的每个更改做出反应。

编辑 — 从 class

外部对 didSet 做出反应

如果您想响应来自 MyObj 外部的更改,我建议您使用闭包:

class MyObj {
    var status: Status = Status(errCode: 0, msg: "Init") {
        didSet {
            statusDidChange?(status)
        }
    }
    
    // closure to call when status changed
    var statusDidChange: ((Status) -> Void)? = nil
    
    struct Status {
        var errCode:Int
        var msg:String
        
        mutating func set(err:Int, txt:String){
            errCode = err
            msg = txt
        }
    }
}

这样,您可以从外部分配一个闭包来执行自定义操作:

let obj = MyObj()
obj.statusDidChange = { status in
    // status did change
    print("new error code: \(status.errCode)")
}

obj.status.set(err: 10, txt: "error 10") // prints "new error code: 10"

编辑 2 — 直接从初始化调用 didSet 闭包

您也可以在初始化期间手动调用 statusDidChange 闭包。

class MyObj {
    var status: Status = Status(errCode: 0, msg: "Init") {
        didSet {
            statusDidChange(status)
        }
    }
    
    // closure to call when status changed
    var statusDidChange: (Status) -> Void
    
    init(status: Status, statusDidChange: @escaping (Status) -> Void) {
        self.status = status
        self.statusDidChange = statusDidChange
        self.statusDidChange(status)
    }
}

let obj = MyObj(status: MyObj.Status(errCode: 9, msg: "error 09")) { status in
    // status did change
    print("new error code: \(status.errCode)")
}

obj.status.set(err: 10, txt: "error 10")

这将打印

new error code: 9
new error code: 10