如果未设置变量,则等待变量设置

时间:2017-12-14 19:34:35

标签: ios swift multithreading asynchronous delegates

我有2个委托方法,这些方法是通过来自第三方库的通知调用的。

方法1: mediaContentWasUpdated()

方法2: adMediaDidBeginPlaying()

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

在方法2中,我们检查adDuration,如果它大于0,那么我们会更新用户界面,以反映我们实际上是在播放广告。

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

我目前对解决方案的尝试是使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()
}

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

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

1 个答案:

答案 0 :(得分:1)

你对NSCondition的讨论让我和你在同一条轨道上,我使用DispatchGroup构建了两个或三个解决方案(这是更好的工具),但是他们总是有很少的角落情况可能表现得很糟糕,而且没有真的抓住了意图。

(如果您对DispatchGroup解决方案感兴趣,可以选择以下形式:在.enter()中致电init,在持续时间到来时致电.leave(),致电{{1当播放开始时。它工作正常,但它会引入可能崩溃的极端情况,就像notify()一样。)

回到真正的意图:

  

在知道持续时间且广告已开始播放时更新用户界面。

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

所以我想到了如何在GCD之前解决这个问题。答案显而易见:只需检查您是否拥有所需的数据,然后再进行操作。 (阅读评论,我看到Paulw11也指出了这一点。)

我个人喜欢将这种东西拉成自己的类型,以使事物更加独立。我讨厌这里的一些名字,但这个想法应该是明确的:

NSCondition

设置每个内容时,看看是否准备好更新,如果是,请更新。我已经摆脱了可选项,因为我认为意图是“0持续时间总是错误的”。但是您可以使用Optional,这样您就可以检测到实际从通知中收到0。

有了这个,你只需设置一个玩家属性:

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
    }
}

(请注意,以上内容可能会创建一个保留循环,具体取决于player = AdPlayer(completion: updateUIForAd) ;您可能需要updateUIForAd闭包等。)

然后根据需要更新它:

[weak self]

创建func mediaContentWasUpdated(notification: NSNotificiation) { player.setDuration(from: notification) } func adMediaDidBeginPlaying(notification: NSNotification) { player.play() } 类型的一大优势是,在广告完成后(或出现问题时)可以轻松重置系统。扔掉整个物体然后再制造另一个物体。