25239


処理が一定時間経ったらタイムアウトさせたいと思い、以下のソースをswiftで書き換えてみました。  
[dispatchのcancelの方法を実践してみる](http://kiwamunet.hateblo.jp/entry/2014/10/25/195913
"dispatchのcancelの方法を実践してみる")

[code]

    var source: dispatch_source_t?    
    
    func setTimer(date: NSDate, block:() -> ()) {
    
        // deltaIntervalを取得してあげる
        let deltaInterval: NSTimeInterval = date.timeIntervalSinceDate(NSDate())
        asyncAfterDelay(deltaInterval, block: block)
    }
    
    func cancelTimer() {
    
        print("キャンセルロジック外側に入りました。")
        if source != nil {
            print("キャンセルロジックに入りました。")
            dispatch_source_cancel(source!)
        }
    }
    
    func asyncAfterDelay(delay: NSTimeInterval, block:() -> ()) {
    
        return asyncAfterDate(NSDate(timeIntervalSinceNow: delay), block: block)
    }
    
    func asyncAfterDate(date: NSDate, block:() -> ()) {
    
        let queue: dispatch_queue_t = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL)
    
        source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
    
        dispatch_source_set_event_handler(source!, {
            dispatch_async(dispatch_get_main_queue(), {
                // 時間になったら呼ばれるもの
                block()
                // 呼ばれたら処理をキャンセルする
                if self.source != nil {
                    dispatch_source_cancel(self.source!)
                }
            })
        })
    
        let delta: dispatch_time_t = getDiapatchTimeByDate(date)
        dispatch_source_set_timer(source!, delta, NSEC_PER_SEC, 0)
        dispatch_resume(source!)
    }
    
    func getDiapatchTimeByDate(date: NSDate) -> dispatch_time_t {
    
        var interval: NSTimeInterval
        var second = Double()
        var subsecond = Double()
        var time = timespec()
        var milestone: dispatch_time_t
    
        interval = date.timeIntervalSince1970
        subsecond = modf(interval, &second)
        time.tv_sec = __darwin_time_t(second)
        time.tv_nsec = Int(subsecond) * Int(NSEC_PER_SEC)
        milestone = dispatch_walltime(&time, 0)
    
        return milestone
    }
    
[/code]

そして以下の内容で動作を確認してみました。  
しかし予定では3秒後にタイムアウトするはずが一向にならずループから抜けられません・・・

[code]

    func doReadInputStream(connectTime: Int) -> Int {
    
        // connectTime = 30
    
        // 現在日時にconnectTime/10の値を足した日時を取得
        let df = NSDateFormatter()
        df.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
        let jpLocale = NSLocale(localeIdentifier: "ja_JP")
    
        df.locale = jpLocale
        df.dateFormat = "yyyyMMddHHmmssS"
        let now = df.stringFromDate(NSDate())
    
        let fmt = NSDateFormatter()
        fmt.calendar = NSCalendar.init(calendarIdentifier: NSCalendarIdentifierGregorian)
        fmt.dateFormat = "yyyyMMddHHmmssS"
        var date = fmt.dateFromString(now)
    
        let dComp = NSDateComponents()
        let connectNum = connectTime / 10
        dComp.second = connectNum
        date = NSCalendar.currentCalendar().dateByAddingComponents(dComp, toDate: date!, options: NSCalendarOptions())
        if let uwDate = date {
            fmt.stringFromDate(uwDate)
        } else {
            print("dateがnilです。")
        }
    
        // タイマーセット
        var isKeika = true
        setTimer(date!, block: {
    
            isKeika = false
        })
    
        // 以下のループを一定時間経ったら抜けたい
        var num = 1
        while isKeika == true {
    
            print("ループ:\(num)回目")
            num += 1
        }
    
        // ループを抜けた後の処理
    
[/code]

書き換えを間違っている、もしくは足りない記述などがあるのでしょうか？


CC BY-SA 3.0
2016-05-27T09:20:48.340
25221
2016-05-28T07:56:18.140
14782
question
swift xcode swift2
処理中に一定時間経ったらキャンセルさせる場合


以下の部分で、メインキューにディスパッチしているので、メインスレッドが空かない限りはここで処理を待つことになります。  
そしてメインスレッドは`doReadInputStream()`メソッドの最後で無限ループを待っているので空くことはありません。

[code]

    dispatch_source_set_event_handler(source!, {
        dispatch_async(dispatch_get_main_queue(), { // メインスレッドは無限ループしているので呼ばれない
            // 時間になったら呼ばれるもの
            block()
            // 呼ばれたら処理をキャンセルする
            if self.source != nil {
                dispatch_source_cancel(self.source!)
            }
        })
    })
    
[/code]

解決するには、メインキューにディスパッチするのをやめるか、

[code]

    dispatch_source_set_event_handler(source!, {
        // 時間になったら呼ばれるもの
        block()
        // 呼ばれたら処理をキャンセルする
        if self.source != nil {
            dispatch_source_cancel(self.source!)
        }
    })
    
[/code]

以下のように、無限ループの中で適度にランループを回してあげる、の２つの方法があります。

[code]

    while isKeika == true {
        print("ループ:\(num)回目")
        num += 1
        NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.01))
    }
    
[/code]


CC BY-SA 3.0
2016-05-28T07:56:18.140
25239
2016-05-28T07:56:18.140
5519
25221
answer