2013-07-04 30 views
34

Tôi có một phương thức hỗ trợ được gọi từ bất kỳ hàng đợi nào và sẽ được mong đợi. Nó chạy một số mã trong một chuỗi nền chính nó, và sau đó sử dụng dispatch_get_main_queue khi nó trả về một giá trị cho đối số khối của nó. Tôi không muốn nó buộc nó vào hàng đợi chính nếu nó không phải là khi nó bước vào phương pháp. Có cách nào để có được một con trỏ đến hàng đợi công văn hiện tại?Nhận hàng đợi công văn hiện tại?

Trả lời

20

Bạn không có quyền lựa chọn "dispatch_get_current_queue()", tuy nhiên iOS 6.1 SDK định nghĩa API này với những từ chối về quyền:

"Recommended for debugging and logging purposes only:"

"This function is deprecated and will be removed in a future release.".

Here's another related question with some alternatives bạn có thể xem xét xem bạn có muốn mã tương lai không.

+2

Điều đó không được chấp nhận? –

+1

bạn nói đúng, Martin! Tôi sẽ làm lại câu trả lời của tôi một chút. –

25

Với việc ngừng sử dụng dispatch_get_current_queue(), không có cách nào để biết bạn đang thực thi hàng đợi nào. Nếu bạn nghiên cứu các GCD sources, cuối cùng bạn sẽ thấy rằng điều này là bởi vì có thể có nhiều câu trả lời cho câu hỏi "những gì hàng đợi tôi thực hiện trên?" (Vì hàng đợi cuối cùng nhắm mục tiêu một trong các hàng đợi toàn cầu, v.v.)

Nếu bạn muốn đảm bảo rằng một khối tương lai được chạy trên hàng đợi cụ thể, thì cách duy nhất là làm cho API của bạn chấp nhận hàng đợi làm thông số với khối hoàn thành. Điều này cho phép người gọi quyết định nơi hoàn thành được thực hiện.

Nếu chỉ biết liệu người gọi có nằm trên sợi chính hay không là đủ, bạn có thể sử dụng +[NSThread isMainThread] để tìm hiểu. Trong trường hợp thông thường, tất cả các khối thực hiện trên hàng đợi GCD chính sẽ được thực thi trên luồng chính. (Một ngoại lệ cho quy tắc này là nếu ứng dụng của bạn sử dụng dispatch_main() thay vì vòng lặp chạy chính, bạn sẽ phải sử dụng dispatch_get_specific và bạn bè để phát hiện chắc chắn rằng bạn đang thực hiện trên hàng đợi chính - đây là trường hợp tương đối hiếm.) Thông thường, lưu ý rằng không phải tất cả các mã thực thi trên luồng chính đều thực hiện trên hàng đợi chính thông qua GCD; GCD là cấp dưới của chuỗi chủ đề chính. Đối với trường hợp cụ thể của bạn có vẻ như đó có thể là đủ.

17

Bạn có thể sử dụng NSOperationQueue cho điều đó. NSOperationQueue có hàm lớp [NSOperationQueue currentQueue], trả về hàng đợi hiện tại làm đối tượng NSOperationQueue. Để nhận được đối tượng hàng đợi công văn, bạn có thể sử dụng [NSOperationQueue currentQueue].underlyingQueue, trả về hàng đợi dòng của bạn dưới dạng dispatch_queue_t.

Swift 3:

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

- làm việc cho hàng đợi chính!

+0

Lưu ý rằng basicQueue được thêm vào trong iOS 8.0. –

+8

Tôi đã chơi xung quanh với điều này, và nó không có vẻ là bạn sẽ nhận được giá trị trả về đúng cho '[NSOperationQueue currentQueue]' nếu mã của bạn đang thực hiện trong một hàng đợi GCD không liên kết với một 'NSOperationQueue'. Nói cách khác, nếu tôi thực hiện một khối trong hàng đợi GCD trực tiếp và tôi gọi '[NSOperationQueue currentQueue] .underlyingQueue' từ bên trong khối đó, tôi không bao giờ có cùng giá trị với hàng đợi thực tế mà tôi đang thực thi khối. – emaloney

+2

Tôi nghĩ @emaloney là đúng. Tôi đã làm một thử nghiệm chạy một khối nhiệm vụ gcd với 'dispatch_sync (myqueue,^{})' và '[NSOperationQueue currentQueue]' trả về hàng đợi chính. –

10

Với việc không dùng của dispatch_get_current_queue() bạn không thể trực tiếp nhận được một con trỏ vào hàng đợi bạn đang chạy trên, tuy nhiên bạn làm có thể nhận được nhãn hàng đợi hiện nay của bằng cách gọi dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) và mà không cung cấp cho bạn một số tính linh hoạt.

Bạn luôn có thể kiểm tra xem bạn có nằm trong hàng đợi cụ thể đó hay không bằng cách so sánh nhãn của họ, trong trường hợp của bạn nếu bạn không muốn ép buộc hàng đợi chính, khi bạn đã nhập phương pháp, bạn có thể sử dụng cờ sau :

let isOnMainQueue = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)) 

Nếu bạn đang chạy trên hàng đợi toàn cầu, bạn sẽ trân trọng có được nhãn của hàng đợi liên kết với nó là loại QOS, mà có thể là một trong những cách sau:

com.apple.root.user-interactive-qos //qos_class_t(rawValue: 33) 
com.apple.root.user-initiated-qos //qos_class_t(rawValue: 25) 
com.apple.root.default-qos   //qos_class_t(rawValue: 21) 
com.apple.root.utility-qos   //qos_class_t(rawValue: 17) 
com.apple.root.background-qos  //qos_class_t(rawValue: 9) 

Và sau đó bạn có thể sử dụng dispatch_get_global_queue(qos_class_self(), 0) sẽ trả lại cho bạn hàng đợi lobal bạn đang chạy.

Nhưng tôi tin rằng Apple đặc biệt không khuyến khích chúng tôi chặn logic vào hàng đợi mà chúng tôi đã gọi, vì vậy tốt hơn nên sử dụng mục đích này cho mục đích gỡ lỗi độc quyền.

+0

Tôi thấy phương pháp này không đáng tin cậy. Theo tài liệu, nhãn là một tham số tùy chọn, vì vậy nó có thể là NULL. dispatch_queue_get_label() trả về một chuỗi rỗng nếu không có nhãn nào được cung cấp khi tạo. – soflare

+0

đó là sự thật, nó chỉ là một cách giải quyết, như tôi đã nói - để thử nghiệm và gỡ lỗi nó có thể đặc biệt hữu ích, nhưng ràng buộc logic trong mã không phải là một ý tưởng hay .. – ambientlight

+0

Vì cách giải quyết có vẻ là phương pháp được biết đến nhiều nhất, tôi có để sử dụng nó để xác định xem hàng đợi hiện tại có phải là một hàng đợi nối tiếp cụ thể hay không. Nếu có, chỉ cần gọi khối trực tiếp thay vì gọi dispatch_sync gây ra khóa chết. Để tránh trường hợp góc tôi đã đề cập trước đây. Tôi chỉ đơn giản là từ chối bất kỳ hàng đợi mà không có một nhãn. – soflare

1

Là phương pháp thay thế cho phương pháp NSOBjectperformSelector:withObject:afterDelay: gửi cuộc gọi đến vòng lặp chạy của chuỗi hiện tại. Theo tài liệu:

Phương pháp này thiết lập bộ hẹn giờ để thực hiện thông báo aSelector trên vòng chạy của luồng hiện tại.

Rõ ràng tôi đang đề xuất sử dụng này với một sự chậm trễ của số không, mà, theo các tài liệu một lần nữa:

Xác định một sự chậm trễ từ 0 không nhất thiết gây ra selector được thực hiện ngay lập tức. Công cụ chọn vẫn được xếp hàng đợi trên vòng lặp hoạt động của luồng và được thực hiện càng sớm càng tốt.

Thật không may nó đòi hỏi chính xác một đối số, vì vậy một số cách giải quyết có thể cần thiết nếu phương pháp của bạn mất nhiều hơn hoặc ít hơn.

Một điều khác tôi lưu ý là phương pháp này không có sẵn cho các giao thức, nhưng chỉ triển khai. Điều này là do phương pháp này thuộc loại NSObject và không nằm trong giao diện NSObject (xem PS bên dưới). Điều này có thể dễ dàng được khắc phục bằng cách truyền tới id.

PS: Hai khác nhau NSObject s tồn tại, một giao thức và triển khai. Chú ý NSObject khai:

@interface NSObject <NSObject> { ... } 

Nó có vẻ kỳ quặc, nhưng là người ta đang tuyên bố (sau khi @interface) và một trong những khác là một giao thức tuyên bố trước đó (giữa <>). Khi khai báo một giao thức mở rộng NSObject (nghĩa là, @protocol Foo <NSObject>), giao thức kế thừa các phương thức từ sau này, nhưng không phải là giao thức trước đây. Cuối cùng, giao thức được thực hiện bởi một số lớp thừa kế từ việc triển khai thực hiện NSObject, vì vậy tất cả các trường hợp kế thừa từ việc triển khai NSObject vẫn được giữ. Nhưng tôi đang rời khỏi chủ đề.

1

Thực ra vẫn còn một cách để so sánh hàng đợi.

Khi bạn thiết lập hàng đợi, hãy đảm bảo rằng bạn thêm nhãn. Đối với mục đích của tôi, tôi có một hàng đợi được chia sẻ được sử dụng để truy cập cơ sở dữ liệu để ngăn chặn khóa cơ sở dữ liệu. Trong DB của tôi.m tệp tôi đã xác định chức năng hàng đợi được chia sẻ như:

const char *kTransactionQueueLabel = "DB_TRANSACTION_DISPATCH_QUEUE"; 

+ (dispatch_queue_t)sharedDBTransactionQueue { 
    static dispatch_queue_t sharedDBQueue = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     sharedDBQueue = dispatch_queue_create(kTransactionQueueLabel, DISPATCH_QUEUE_SERIAL); 
    }); 

    return sharedDBQueue; 
} 

Hàng đợi giao dịch db được chia sẻ được sử dụng cục bộ trong tệp để gửi tất cả các thực thi đến cơ sở dữ liệu. Tuy nhiên, cũng có một người truy cập công khai về điều này để cho phép gửi toàn bộ các giao dịch đến cơ sở dữ liệu. Vì vậy, trong nội bộ, nếu một phương thức truy cập DB được gọi từ bên trong hàng đợi giao dịch, chúng ta cần gửi nội bộ trên một hàng đợi khác (tất cả các công văn đồng bộ). Vì vậy, trong nội bộ, tôi luôn gửi đi hàng đợi thích hợp bằng cách sử dụng trình rút gọn bên dưới.

/** 
* @description Decide which queue to use - if we are already in a transaction, use the internal access queue, otherwise use the shared transaction queue. 
*/ 
- (dispatch_queue_t)getProperQueueForExecution { 
    const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); 
    dispatch_queue_t sharedAccessQueue = [DB sharedDBTransactionQueue]; 
    if (strcmp(currentLabel, kTransactionQueueLabel) == 0) { 
     sharedAccessQueue = [DB sharedInternalDBAccessQueue]; 
    } 

    return sharedAccessQueue; 
} 

Hy vọng điều này sẽ hữu ích. Xin lỗi cho ví dụ dài. Gist của nó là bạn có thể sử dụng

const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); 

để nhận nhãn của hàng đợi hiện tại và so sánh với nhãn đã xác định.

0

Tôi có cùng các yêu cầu chức năng mà bài đăng gốc đề cập đến. Bạn có thể gọi hàm async này trên bất kỳ hàng đợi nào, nhưng nếu được gọi trên hàng đợi chính, sau đó gọi lại cho người dùng trên hàng đợi chính. Tôi chỉ cần xử lý nó như vậy:

// cache value for if we should callback on main queue 
BOOL callbackOnMT = [NSThread isMainThread]; 

// ... 
// ... do async work... 
// ... 

if (callbackOnMT && ![NSThread isMainThread]){ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     // callback to user on main queue 
     // as they called this function on main queue 
     callbackToUser(); 
    }); 
} 
else{ 
    // callback to user on our current queue 
    // as they called this function on a non-main queue 
    callbackToUser(); 
} 
Các vấn đề liên quan