2017-12-13 138 views
7

Tôi nhận được hai loại thông tin với JSON và tôi đang thêm "hoạt động" vào 2 Lớp hoạt động xếp hàng khác nhau với addObserver (forKeyPath: "operations" ...) . Trong hàm observValue tôi đang kiểm tra xem operationQueue1.operations.isEmpty và sau đó tôi làm mới thông tin của mình trong giao diện người dùng. Tôi đang làm điều tương tự với if else với operationQueue2, nhưng khi 2 hoạt động được bắt đầu trong đôi khi các ứng dụng sụp đổ với thông báo lỗi: *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <AppName.ViewController 0x102977800> for the key path "operations" from <AppName.OperationQueue1 0x1c4a233c0> because it is not registered as an observer. ' Tôi không gặp vấn đề gì khi chỉ có 1 thao tác được bắt đầu. Bất kỳ đề xuất?Swift - Lỗi ứng dụng khi sử dụng hai OperationQueues khác nhau với KVO

func getInfo1(){//runned in viewDidLoad 
    operationQueue1.addObserver(forKeyPath:"operations"...) 
    operationQueue1.dataTask(URL:"..."....){ 
     DispatchQueue.main.async{ 
    NotificationCenter.default.postNotification(NSNotification.Name(rawValue: "NewDataReceived1", userInfo:infoFromTheWebsite) 
     } 
    } 
} 

func NewDataReceived1(){ 
    here I add the information to arrays to be loaded in tableView1 
} 

HERE IS THE CODE FOR 2ND INFO WHICH IS THE SAME 

override func observeValue(forKeyPath keyPath: String?, ....){ 
     if(object as? operationQueue1 == operationQueue1Class && keyPath == "operations" && context == context1){ 
      if(operationQueue1.operations.isEmpty){ 
        DispatchQueue.main.async{ 
         operationQueue1..removeObserver(self, forKeyPath:"operations") 
         Timer.scheduled("refreshingTableInformation1") 
        } 
      } 
     }else if(operationQueue2....){ 
      SAME AS OPERATION 1, BUT USING DIFFERENT FUNC TO REFRESH TABLE INFORMATION AND THE TABLES ARE DIFFERENT 
     }else{ 
      super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) 
     } 
} 

func refreshingTableInformation1(){ 
    tableView1.reloadData() 
    Timer.scheduled("getInfo1", repeat:false) 
} 

func refreshingTableInformation2(){ 
    tableView2.reloadData() 
    Timer.scheduled("getInfo2", repeat:false) 
} 

Đôi khi nó hoạt động 10 giây và sụp đổ và đôi khi làm việc cho hơn 60 giây và sau đó sụp đổ ...

+0

Giống như lỗi nói, bạn đang cố gắng xóa bộ điều khiển chế độ xem khỏi quan sát đường phím hoạt động của 'OperationQueue1' mặc dù trình điều khiển chế độ xem không được đăng ký làm người quan sát. Điều này có thể xảy ra nếu bạn tham chiếu đến vc đã thay đổi. Đăng mã của hàng đợi hoạt động của bạn và nơi bạn đang thêm người quan sát. –

+0

Tôi đang thêm người quan sát trước 2 yêu cầu và họ bị khóa trong vòng kết nối vô hạn. Khi tôi cập nhật thông tin trong giao diện người dùng, tôi đặt lại người quan sát và tôi bắt đầu yêu cầu nhận lại thông tin. –

+0

Mã của bạn không rõ ràng. Dù sao, tại sao không đưa một người quan sát vào mảng thu thập 'NewDataReceived1' thay thế? sẽ dễ xử lý hơn. Ngoài ra, nếu bạn gặp sự cố đồng bộ hóa, tốt hơn bạn nên sử dụng các lib như Alamofire cho các yêu cầu http của bạn. Điều này sẽ phụ tùng cho bạn rất nhiều rắc rối. – Alex

Trả lời

4

Mã của bạn ở đây là dễ bị tổn thương để đua điều kiện. Hãy xem xét trường hợp sau đây:

    getInfo1() được gọi, thêm hoạt động vào operationQueue1.

  1. Thao tác hoàn tất, có nghĩa là quan sát KVO của bạn được gọi. Hàng đợi hiện đang trống, vì vậy lịch trình quan sát của bạn sẽ loại bỏ người quan sát của bạn trên hàng đợi công văn chính.

  2. Bây giờ, trước khi thao tác bạn đã gửi đến hàng đợi chính có thể chạy, một số khác gọi là getInfo1(), thêm hoạt động mới vào operationQueue1, hoàn tất trước khi thao tác bạn xếp hàng ở bước 2 đã có cơ hội để chạy (này, có thể hàng đợi chính đang bận với thứ gì đó; điều này dễ xảy ra vì nó là hàng đợi nối tiếp).

  3. quan sát của bạn cho cuộc gọi đầu tiên của getInfo1() được gọi lại trong khi hàng đợi rỗng, gây một khối xoá đăng ký phải nộp vào hàng đợi chính.

  4. Hai khối đăng ký cuối cùng sẽ được thực hiện trên hàng đợi chính. Chương trình thứ hai sẽ bị treo chương trình vì bạn đã hủy đăng ký người quan sát của mình.

Bạn có thể có thể khắc phục vấn đề này (giả sử mã không có nhiều vấn đề hơn của thiên nhiên này) bằng cách sử dụng các quan sát viên khối dựa trên Swift 4 thay thế, và thiết lập các quan sát viên đến nil thay vì rõ ràng deregistering nó. Tuy nhiên, tôi đề nghị KVO là wrong tool cho những gì bạn đang cố gắng làm. Theo hướng dẫn cho trò chơi "Crystal Quest" cũ được sử dụng để nói, nó giống như sử dụng súng chống máy bay để giết một con muỗi.

Từ những gì tôi có thể thấy từ mã ở trên, có vẻ như bạn đang sử dụng KVO chỉ để lên lịch thông báo khi hoạt động hoặc nhóm hoạt động bạn gửi đến hàng đợi kết thúc. Tùy thuộc vào những gì phương pháp dataTask của bạn thực sự không có gì, đây là những gì tôi muốn làm thay vì:

  • Nếu bạn chỉ gửi một hoạt động: thiết lập thuộc tính completionBlock của hoạt động để đóng cửa mà làm mới thông tin bảng của bạn.

  • Nếu bạn gửi nhiều hơn một thao tác: Tạo BlockOperation mới làm mới thông tin bảng của bạn và gọi addDependency trên thao tác đó với mọi thao tác khác bạn gửi đến hàng đợi. Sau đó, gửi hoạt động đó.

Điều này sẽ cung cấp cho bạn cách hoàn thiện hơn và không có rắc rối để giám sát hoàn thành nhiệm vụ của bạn. Và vì bạn không cần hàng đợi để trống hoàn toàn nữa, bạn thậm chí có thể không phải sử dụng hai hàng đợi riêng biệt nữa, tùy thuộc vào việc bạn đang làm gì với chúng nữa.

+0

Tôi đang sử dụng KVO, vì tôi có chế độ xem bảng động và nếu trang web không trả lại cho tôi thông tin tôi không tạo chế độ xem bảng –

+0

@BogdanBogdanov Bạn có thể làm rõ hơn về chính xác bạn đang làm gì không? Tôi không hiểu tại sao trì hoãn việc tạo ra một cái nhìn bảng sẽ đòi hỏi phải sử dụng KVO trên hàng đợi hoạt động thay vì chỉ làm nó trong một hoạt động hoàn thành. –

+0

Tôi đồng ý với @CharlesSrstka, suy nghĩ đầu tiên của tôi là một điều kiện chủng tộc và đặc biệt là số 4 trong danh sách của anh ấy ở trên. Với mới hơn 'tableView.beginUpdates()' và '.endUpdates()' hoặc 'performBatchUpdates (_: completion:)' làm cho nó có thể cập nhật tableView từ changeHandler của một người quan sát khối. lưu ý rằng 'quan sát (_: options: changeHandler:)' các cuộc gọi 'addObserver_: forKeyPath: options: context:)' và 'removeObserver (_: forKeyPath: context:)' khi cần thiết. – RLoniello

Các vấn đề liên quan