Các lựa chọn thay thế cho disp_get_current_queue () cho các khối hoàn thành trong iOS 6?


101

Tôi có một phương thức chấp nhận một khối và một khối hoàn thành. Khối đầu tiên phải chạy trong nền, trong khi khối hoàn thành sẽ chạy trong bất kỳ hàng đợi nào mà phương thức được gọi.

Đối với cái sau mà tôi luôn sử dụng dispatch_get_current_queue(), nhưng có vẻ như nó không còn được dùng trong iOS 6 trở lên. Tôi nên sử dụng gì để thay thế?


tại sao bạn nói dispatch_get_current_queue()là không được chấp nhận trong iOS 6? các tài liệu nói gì về nó
Jere

3
Trình biên dịch phàn nàn về nó. Thử nó.
cfischer

4
@jere Kiểm tra tệp tiêu đề, nó nói rằng nó bị
mô tả

Ngoài các cuộc thảo luận về phương pháp hay nhất là gì, tôi thấy [NSOperationQueue currentQueue] có thể trả lời câu hỏi. Không chắc chắn về những lưu ý khi sử dụng nó.
Matt

báo trước được tìm thấy ------ [NSOperationQueue currentQueue] không giống như Disp_get_current_queue () ----- Đôi khi trả về giá trị null ---- accept_async (disp_get_global_queue (0, 0), ^ {NSLog (@ "q (0, 0) là% @ ", disp_get_current_queue ()); NSLog (@" cq (0,0) is% @ ", [NSOperationQueue currentQueue]);}); ----- q (0,0) là <OS_dispatch_queue_root: com.apple.root.default-qos [0x100195140]> cq (0,0) is (null) ----- depricated or not Dispatch_get_current_queue () dường như là giải pháp duy nhất tôi thấy để báo cáo hàng đợi hiện hành trong mọi điều kiện
godzilla

Câu trả lời:


64

Mô hình "chạy trên bất kỳ hàng đợi nào mà người gọi đang ở" rất hấp dẫn, nhưng cuối cùng không phải là một ý tưởng tuyệt vời. Hàng đợi đó có thể là hàng đợi có mức độ ưu tiên thấp, hàng đợi chính hoặc một số hàng đợi khác có thuộc tính lẻ.

Cách tiếp cận yêu thích của tôi đối với điều này là nói "khối hoàn thành chạy trên hàng đợi được xác định triển khai với các thuộc tính sau: x, y, z" và để khối gửi đến một hàng đợi cụ thể nếu người gọi muốn kiểm soát nhiều hơn thế. Một tập hợp các thuộc tính điển hình để chỉ định sẽ là một cái gì đó như "nối tiếp, không đăng nhập lại và không đồng bộ đối với bất kỳ hàng đợi ứng dụng nào khác".

** BIÊN TẬP **

Catfish_Man đưa một ví dụ vào phần bình luận bên dưới, tôi chỉ thêm nó vào câu trả lời của anh ấy.

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}

7
Hoàn toàn đồng ý. Bạn có thể thấy rằng Apple luôn tuân theo điều này; bất cứ khi nào bạn muốn thực hiện điều gì đó trên hàng đợi chính, bạn luôn cần gửi đến hàng đợi chính vì apple luôn đảm bảo rằng bạn đang ở một chuỗi khác. Hầu hết thời gian, bạn đang đợi một quá trình chạy dài kết thúc tìm nạp / thao tác dữ liệu và sau đó bạn có thể xử lý nó trong nền ngay trong khối hoàn thành của mình và sau đó chỉ gắn các lệnh gọi giao diện người dùng vào một khối điều phối trên hàng đợi chính. Thêm vào đó, luôn tốt khi làm theo những gì Apple đặt ra về kỳ vọng vì các nhà phát triển sẽ quen với mô hình này.
Jack Lawrence

1
câu trả lời tuyệt vời .. nhưng tôi đã hy vọng ít nhất một số mẫu mã để minh họa cho những gì bạn đang nói
abbood

3
- (void) aMethodWithCompletionBlock: (dispatch_block_t) completionHandler {dispatch_async (self.workQueue, ^ {[tự doSomeWork]; dispatch_async (self.callbackQueue, completionHandler);}}
Catfish_Man

(Đối với một ví dụ hoàn toàn tầm thường)
Catfish_Man

3
Điều này không thể xảy ra trong trường hợp chung vì có thể (và trên thực tế là khá có khả năng) nằm trên nhiều hàng đợi đồng thời, do disp_sync () và dict_set_target_queue (). Có thể có một số tập hợp con của trường hợp chung.
Catfish_Man

27

Về cơ bản, đây là cách tiếp cận sai đối với API mà bạn đang mô tả. Nếu một API chấp nhận một khối và một khối hoàn thành để chạy, thì các thông tin sau cần phải đúng:

  1. "Khối để chạy" phải được chạy trên một hàng đợi nội bộ, ví dụ: một hàng đợi riêng tư đối với API và do đó hoàn toàn nằm dưới sự kiểm soát của API đó. Ngoại lệ duy nhất cho điều này là nếu API tuyên bố cụ thể rằng khối sẽ được chạy trên hàng đợi chính hoặc một trong các hàng đợi đồng thời toàn cầu.

  2. Khối hoàn thành phải luôn được thể hiện dưới dạng một bộ (hàng đợi, khối) trừ khi các giả định tương tự như đối với # 1 đúng, ví dụ: khối hoàn thành sẽ được chạy trên một hàng đợi chung đã biết. Ngoài ra, khối hoàn thành phải được gửi đi không đồng bộ trên hàng đợi đã chuyển.

Đây không chỉ là những điểm về phong cách, chúng hoàn toàn cần thiết nếu API của bạn được an toàn khỏi các deadlock hoặc các hành vi cạnh tranh khác SẼ treo cổ bạn khỏi cái cây gần nhất vào một ngày nào đó. :-)


11
Âm thanh lý, nhưng vì một lý do đó không phải là cách tiếp cận của Apple cho các API riêng của họ: hầu hết các phương pháp mà phải mất một khối hoàn thành không còn lấy một hàng đợi ...
cfischer

2
Đúng, và để sửa đổi phần nào khẳng định trước đây của tôi, nếu rõ ràng là khối hoàn thành sẽ được chạy trên hàng đợi chính hoặc hàng đợi đồng thời toàn cục. Tôi sẽ thay đổi câu trả lời của mình để chỉ ra càng nhiều.
jkh

Để nhận xét về việc Apple không áp dụng cách tiếp cận đó: Apple không phải lúc nào cũng "đúng" theo định nghĩa. Lập luận đúng đắn luôn mạnh hơn bất kỳ thẩm quyền cụ thể nào, điều mà bất kỳ nhà khoa học nào cũng sẽ xác nhận. Tôi nghĩ câu trả lời ở trên nói lên điều đó rất tốt từ góc độ kiến ​​trúc phần mềm thích hợp.
Werner Altewischer 20/09/18

14

Các câu trả lời khác rất tuyệt, nhưng đối với tôi thì câu trả lời là cấu trúc. Tôi có một phương pháp như thế này trên Singleton:

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

có hai phần phụ thuộc, đó là:

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

typedef void (^simplest_block)(void); // also could use dispatch_block_t

Bằng cách đó, tôi tập trung các cuộc gọi của mình để gửi trên luồng khác.


12

Bạn nên cẩn thận về việc sử dụng của bạn dispatch_get_current_queuengay từ đầu. Từ tệp tiêu đề:

Chỉ được đề xuất cho mục đích gỡ lỗi và ghi nhật ký:

Mã không được đưa ra bất kỳ giả định nào về hàng đợi được trả về, trừ khi nó là một trong các hàng đợi toàn cục hoặc hàng đợi mà mã đã tự tạo. Mã không được giả định rằng thực thi đồng bộ trên một hàng đợi là an toàn khỏi bế tắc nếu hàng đợi đó không phải là hàng được trả về bởi send_get_current_queue ().

Bạn có thể làm một trong hai điều sau:

  1. Giữ một tham chiếu đến hàng đợi bạn đã đăng ban đầu (nếu bạn đã tạo nó qua dispatch_queue_create) và sử dụng nó từ đó trở đi.

  2. Sử dụng hàng đợi do hệ thống xác định thông qua dispatch_get_global_queuevà theo dõi bạn đang sử dụng hàng nào.

Một cách hiệu quả trong khi trước đây dựa vào hệ thống để theo dõi hàng đợi bạn đang xếp, bạn sẽ phải tự mình thực hiện.


16
Làm thế nào chúng tôi có thể "giữ tham chiếu đến hàng đợi mà bạn đã đăng ban đầu" nếu chúng tôi không thể sử dụng dispatch_get_current_queue()để tìm ra hàng đợi đó? Đôi khi mã cần biết hàng đợi mà nó đang chạy lại không có bất kỳ quyền kiểm soát hoặc kiến ​​thức nào về nó. Tôi có rất nhiều mã có thể (và nên) được thực thi trên hàng đợi nền nhưng đôi khi cần cập nhật gui (thanh tiến trình, v.v.), và do đó, cần chuyển_sync () tới hàng đợi chính cho các hoạt động đó. Nếu đã ở trong hàng đợi chính, thì accept_sync () sẽ khóa vĩnh viễn. Tôi sẽ mất hàng tháng để cấu trúc lại mã của mình cho việc này.
Abhi Beckert

3
Tôi nghĩ rằng NSURLConnection cung cấp các lệnh gọi lại hoàn thành của nó trên cùng một luồng mà nó được gọi từ đó. Liệu nó có đang sử dụng cùng một API "Dispatch_get_current_queue" để lưu trữ hàng đợi mà nó được gọi để sử dụng tại thời điểm gọi lại không?
defactodeity

5

Apple đã không dùng nữa dispatch_get_current_queue(), nhưng vẫn để lại một lỗ hổng ở nơi khác, vì vậy chúng tôi vẫn có thể nhận được hàng đợi gửi hiện tại:

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

Điều này ít nhất có hiệu quả đối với hàng đợi chính. Lưu ý, thuộc tính đó underlyingQueuecó sẵn kể từ iOS 8.

Nếu bạn cần thực hiện khối hoàn thành trong hàng đợi ban đầu, bạn cũng có thể sử dụng OperationQueuetrực tiếp, ý tôi là không cần GCD.



0

Đây là một câu trả lời cho tôi. Vì vậy, tôi sẽ nói về trường hợp sử dụng của chúng tôi.

Chúng ta có một lớp dịch vụ và lớp giao diện người dùng (trong số các lớp khác). Lớp dịch vụ chạy các tác vụ trong nền. (Tác vụ thao tác dữ liệu, tác vụ CoreData, Cuộc gọi mạng, v.v.). Lớp dịch vụ có một số hàng đợi hoạt động để đáp ứng nhu cầu của lớp giao diện người dùng.

Lớp giao diện người dùng dựa vào lớp dịch vụ để thực hiện công việc của nó và sau đó chạy một khối hoàn thành thành công. Khối này có thể có mã UIKit trong đó. Một trường hợp sử dụng đơn giản là lấy tất cả thư từ máy chủ và tải lại chế độ xem bộ sưu tập.

Ở đây chúng tôi đảm bảo rằng các khối được chuyển vào lớp dịch vụ được gửi đi trên hàng đợi mà dịch vụ được gọi trên đó. Vì Dispatch_get_current_queue là một phương thức không được dùng nữa, chúng tôi sử dụng NSOperationQueue.currentQueue để lấy hàng đợi hiện tại của người gọi. Lưu ý quan trọng về tài sản này.

Gọi phương thức này từ bên ngoài ngữ cảnh của một hoạt động đang chạy thường dẫn đến kết quả là không được trả về.

Vì chúng tôi luôn gọi các dịch vụ của mình trên một hàng đợi đã biết (Hàng đợi tùy chỉnh của chúng tôi và Hàng đợi chính), điều này hoạt động tốt đối với chúng tôi. Chúng tôi có những trường hợp mà serviceA có thể gọi serviceB có thể gọi serviceC. Vì chúng tôi kiểm soát cuộc gọi dịch vụ đầu tiên được thực hiện từ đâu, chúng tôi biết phần còn lại của các dịch vụ sẽ tuân theo các quy tắc tương tự.

Vì vậy, NSOperationQueue.currentQueue sẽ luôn trả về một trong các Hàng đợi của chúng ta hoặc MainQueue.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.