2017-04-06 46 views
1

在這個簡單的代碼(的Xcode 8.3),創建一個操作子類的實例,註冊其isFinished財產的志願觀測,並通過將其添加到我的隊列啓動操作:當註銷操作的志願觀測isFinished

class MyOperation : Operation { 
    override func main() { 
     print("starting") 
     print("finishing") 
    } 
} 

class ViewController: UIViewController { 
    let q = OperationQueue() 
    override func viewDidLoad() { 
     super.viewDidLoad() 
     let op = MyOperation() 
     op.addObserver(self, forKeyPath: #keyPath(MyOperation.isFinished), options: [], context: nil) 
     self.q.addOperation(op) 
    } 

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
     print("Observed \(keyPath)") 
     if let op = object as? Operation { 
      op.removeObserver(self, forKeyPath: #keyPath(MyOperation.isFinished)) 
     } 
    } 
} 

正如你所看到的,我當然有一個observeValue(forKeyPath...的實現,我的計劃是在那裏調用removeObserver(forKeyPath...

問題是我的應用程序崩潰,「MyOperation被釋放,而關鍵值觀察員仍然註冊它」。我們打印「開始」和「結束」,但我們從不打印「觀察」;該操作在之前不存在我收到了我的KVO通知。

這看起來像一個catch-22。如果我不能通過觀察isFinished刪除觀察者,那麼我應該怎麼做? [我可以通過向MyOperation添加我自己的KVO可觀察屬性來解決此問題,該屬性在main末尾處設置。但我應該這樣做的想法很奇怪,是不是這正是爲什麼isFinished是可觀的,所以我可以做什麼,我想在這裏?]

+1

難道這是https://developer.apple.com/library/content/technotes/tn2109/_index.html中提到的問題嗎? - 「使用鍵值觀察(KVO)觀察NSOperation的isFinished屬性時可能會出現類似的問題,雖然KVO不保留觀察者或觀察者,但即使您刪除了觀察者-viewWillDisappear:方法,KVO通知可能已經在您的對象的運行中,如果發生這種情況,運行通知的線程最終可能會調用一個釋放對象! –

+0

@MartinR本節註釋的是「自我」(觀察者)可能不存在的危險,因此KVO通知可能會發送給不存在的對象。這是我的問題的反映;我的問題是,操作(觀察者)將不存在而不向我發送我的KVO通知。 'self'是根視圖控制器,並且無處可去。 – matt

+0

是否有任何用處提及我嘗試過,我可以看到:'開始完成Observed Optional(「isFinished」)'?我的應用程序不會*崩潰。我正在使用xcode 8.2,我希望我明白情況如何...... –

回答

2

測試完全相同的給定的代碼片段的Xcode 8.2後,它的工作,因爲它應該,控制檯顯示:

starting 
finishing 
Observed Optional("isFinished") 

看來,這個問題的原因是在的Xcode 8.3測試它,可能它是一個錯誤 - 或者它可能是一個新的動作 - 。不過,我建議將它報告爲一個錯誤。

+1

好吧,事實證明,這個錯誤不是可可基金會的錯誤 - 它是一個涉及'#keyPath'的Swift語言錯誤。解決方法是使用文字字符串來代替編寫op.addObserver(self,forKeyPath:「isFinished」,options:[],context:nil)'。 – matt

+0

這裏是Swift錯誤報告:https://bugs.swift.org/browse/SR-4397我也提交了我的,但它被正確地標記爲重複。 – matt

+0

@matt但是什麼原因:我們應該將它作爲'forKeyPath:「isFinished」'而不是'#keyPath(MyOperation.isFinished)'來實現呢?這種情況在我看來與使用'#selector'類似。 –

2

Apple在Swift 3.1中更改了#keyPath行爲(source)。目前#keyPath(isFinished)返回"finished",它用於返回"isFinished"這是一個錯誤。以下是解釋,因爲在使用KVOOperation類時,它可能會很容易混淆。


當您註冊一個對象KVO通知

textView.addObserver(self, 
        forKeyPath: #keyPath(UITextView.isEditable), 
        options: [.new, .old], 
        context: nil) 

Foundation提供了它(textView)與調用willChangeValue(forKey:)didChangeValue(forKey:)this is done via isa-swizzling)新二傳的實現。傳遞給這些方法的密鑰不是獲取者(isEditable)而不是設置者(setEditable:),而是屬性名稱(editable)。

@property(nonatomic,getter=isEditable) BOOL editable 

這就是爲什麼它預計從#keyPath(UITextView.isEditable)接收editable


雖然操作類具有定義廣告是否符合

@property (readonly, getter=isExecuting) BOOL executing; 
@property (readonly, getter=isFinished) BOOL finished; 

它希望遵守通知isFinishedisExecuting鍵。

完成後或者其任務的取消,您的併發操作對象必須生成兩個isExecuting志願通知和isFinished關鍵路徑標記狀態的最終變化爲您的操作

這迫使我們發佈這些通知時使用文字字符串。

恕我直言,這是多年前發生的一個錯誤,如果不破壞現有代碼,很難恢復。

+0

是的,我知道這一切。 – matt