【问题标题】:If variable is not set, wait for variable to be set如果变量没有设置,等待变量被设置
【发布时间】:2017-12-14 19:34:34
【问题描述】:

我有 2 个委托方法,它们被来自 3rd 方库的通知调用。

方法一: mediaContentWasUpdated()

方法二: adMediaDidBeginPlaying()

在方法 1 中,一个关键变量 (adDuration) 是从随通知一起传入的参数中设置的。据我所知,这是获取此信息的唯一地方。

在方法 2 中,我们检查 adDuration,如果它大于 0,则更新 UI 以反映我们实际上正在播放广告。

出现了一个错误,有时这两种方法的调用顺序错误。这意味着 adDuration 未设置,方法 2 认为没有要播放的广告媒体,因此不会相应地更新 UI。

我目前的解决方案尝试是将adDuration 设为可选并使用NSCondition 导致方法2 等待方法1 设置adDuration 然后继续。

var adDuration : Double?
let condition = NSCondition()

func mediaContentWasUpdated(notification: NSNotificiation) {
    condition.lock()

    if(notificationHasAdDurationInfo(notification)) {
        self.adDuration = getAdDuration(notification)
        condition.signal()
    }

    condition.unlock()
}

func adMediaDidBeginPlaying(notification: NSNotification) {
    condition.lock()

    while adDuration == nil {
        condition.wait()
    }

    if adDuration! > Double(0) {
        updateUIForAd()
    }

    condition.unlock()
}

这是我第一次尝试这样的事情,我担心我做错了什么。我还担心不必要地锁定和解锁线程(这会发生在适当的时间运行中,或者如果没有要播放的广告内容)。

外部因素阻碍了我的测试能力,我想在等待这些问题得到解决的同时获得一些意见,看看我是否朝着正确的方向前进。

【问题讨论】:

  • 第一种方法中得到adDuration后为什么不自己调用第二种方法。
  • @SanthoshR 第二种方法也是通知委托方法。只有在有广告即将播放时才应调用它。
  • 我知道,但是如果您在第一种方法中得到的adDuration 大于零,则表示存在广告,然后您可以使用虚假通知调用第二种方法。
  • adDuration 也可能意味着插播广告可用。代码非常简化,方法 2 使用通过其notification 对象发送的信息。
  • 为什么在调用adMediaDidBeginPlaying 时没有一个布尔属性设置为true。然后在两个委托中调用你的“updateUI”函数;如果 duration 为 nil 或标志为 false,则让 updateUI 函数立即返回。一旦你有了一个持续时间和一个标志,更新 UI 并清除标志,为下一次做好准备。这样调用委托方法的顺序无关紧要

标签: ios swift multithreading asynchronous delegates


【解决方案1】:

你对 NSCondition 的讨论让我和你走上了同一条轨道,我使用 DispatchGroup(这是更好的工具)构建了两三个解决方案,但他们总是有一些可能表现不佳的极端情况,并且没有t 真正捕捉到意图。

(如果您对 DispatchGroup 解决方案感兴趣,它们的形式如下:在init 中调用.enter(),在持续时间到来时调用.leave(),在播放开始时调用notify()。它工作正常,但它引入了可能崩溃的极端情况,就像NSCondition。)

回到真正的意图:

当持续时间已知且广告开始播放时更新 UI。

这里没有并发。所以拔出 GCD 不仅仅是矫枉过正。它实际上使事情变得更糟,因为它引入了许多复杂的极端情况。

所以我想在 GCD 之前如何解决这个问题。答案很明显:只需检查您是否拥有所需的数据,然后执行此操作。 (通读 cmets,我看到 Paulw11 也指出了这一点。)

就我个人而言,我喜欢把这种东西拉到自己的类型中,让事情变得更加独立。我讨厌这里的一些名字,但想法应该很清楚:

class AdPlayer {
    private var readyToPlay = false
    private var duration: Double = 0.0
    private let completion: (Double) -> Void

    func setDuration(from notification: Notification) {
        if(notificationHasAdDurationInfo(notification)) {
            duration = getAdDuration(notification)
        }
        playIfReady()
    }

    func play() {
        readyToPlay = true
        playIfReady()
    }

    private func playIfReady() {
        if duration > 0 && readyToPlay {
            completion(duration)
        }
    }

    init(completion: @escaping (Double) -> Void) {
        self.completion = completion
    }
}

当你设置好每个东西时,看看你是否准备好更新,如果是,就更新。我也摆脱了可选项,因为我相信其意图是“0 持续时间总是错误的”。但是您可以使用 Optional,这样您就可以检测到实际从通知中接收到 0。

这样,您只需设置一个播放器属性:

player = AdPlayer(completion: updateUIForAd)

(请注意,上面可能会创建一个保留循环,具体取决于 updateUIForAd 是什么;您可能需要一个 [weak self] 闭包等。)

然后根据需要进行更新:

func mediaContentWasUpdated(notification: NSNotificiation) {
    player.setDuration(from: notification)
}

func adMediaDidBeginPlaying(notification: NSNotification) {
    player.play()
}

创建AdPlayer 类型的一大优势在于,在广告完成(或出现问题时)很容易重置系统。只需扔掉整个对象并创建另一个对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 2013-08-27
    • 2019-02-07
    相关资源
    最近更新 更多