ทางเลือกในการ dispatch_get_current_queue () สำหรับการบล็อกให้เสร็จสมบูรณ์ใน iOS 6?


101

ฉันมีวิธีการที่ยอมรับบล็อกและบล็อกเสร็จสิ้น บล็อกแรกควรทำงานในพื้นหลังในขณะที่บล็อกการเสร็จสมบูรณ์ควรทำงานในคิวใดก็ตามที่เมธอดเรียก

ในช่วงหลังฉันใช้มาตลอดdispatch_get_current_queue()แต่ดูเหมือนว่าจะเลิกใช้งานใน iOS 6 ขึ้นไป ฉันควรใช้อะไรแทน?


ทำไมคุณถึงบอกว่าdispatch_get_current_queue()เลิกใช้งานใน iOS 6 แล้ว? เอกสารไม่ได้พูดอะไรเกี่ยวกับเรื่องนี้
เมื่อ

3
คอมไพเลอร์บ่นเกี่ยวกับเรื่องนี้ ลองมัน.
cfischer

4
@jere ตรวจสอบไฟล์ส่วนหัวมันระบุว่าหมดราคา
WDUK

นอกเหนือจากการอภิปรายเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุดแล้วฉันเห็น [NSOperationQueue currentQueue] ซึ่งอาจตอบคำถามได้ ไม่แน่ใจเกี่ยวกับคำเตือนเกี่ยวกับการใช้งาน
Matt

พบข้อแม้ ------ [NSOperationQueue currentQueue] ไม่เหมือนกับ dispatch_get_current_queue () ----- บางครั้งจะคืนค่า null ---- dispatch_async (dispatch_get_global_queue (0, 0), ^ {NSLog (@ "q (0, 0) คือ% @ ", dispatch_get_current_queue ()); NSLog (@" cq (0,0) คือ% @ ", [NSOperationQueue currentQueue]);}); ----- q (0,0) คือ <OS_dispatch_queue_root: com.apple.root.default-qos [0x100195140]> cq (0,0) เป็น (null) ----- ยกเลิกหรือไม่ dispatch_get_current_queue () ดูเหมือน เพื่อเป็นทางออกเดียวที่ฉันเห็นสำหรับการรายงานคิวปัจจุบันในทุกเงื่อนไข
godzilla

คำตอบ:


64

รูปแบบของ "เรียกใช้ตามคิวใดก็ตามที่ผู้โทรเปิดอยู่" นั้นน่าสนใจ แต่ท้ายที่สุดก็ไม่ใช่ความคิดที่ดี คิวนั้นอาจเป็นคิวลำดับความสำคัญต่ำคิวหลักหรือคิวอื่น ๆ ที่มีคุณสมบัติแปลก ๆ

วิธีที่ฉันชอบที่สุดคือการพูดว่า "การบล็อกเสร็จสิ้นทำงานบนคิวที่กำหนดการนำไปใช้งานด้วยคุณสมบัติเหล่านี้: x, y, z" และปล่อยให้บล็อกส่งไปยังคิวหนึ่งหากผู้เรียกต้องการการควบคุมมากกว่านั้น ชุดคุณสมบัติทั่วไปที่จะระบุจะเป็น "อนุกรมไม่ reentrant และ async เทียบกับคิวอื่น ๆ ที่แอปพลิเคชันมองเห็นได้"

** แก้ไข **

Catfish_Man ใส่ตัวอย่างไว้ในความคิดเห็นด้านล่างฉันแค่เพิ่มคำตอบของเขา

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

7
เห็นด้วยอย่างยิ่ง. คุณจะเห็นว่า Apple ทำตามนี้เสมอ เมื่อใดก็ตามที่คุณต้องการทำบางสิ่งในคิวหลักคุณจะต้องส่งไปยังคิวหลักเสมอเพราะ apple รับประกันเสมอว่าคุณอยู่คนละเธรด เวลาส่วนใหญ่คุณกำลังรอให้กระบวนการทำงานที่ยาวนานเพื่อดึงข้อมูล / จัดการข้อมูลให้เสร็จสิ้นจากนั้นคุณสามารถประมวลผลในพื้นหลังได้ในบล็อกการเสร็จสิ้นของคุณจากนั้นติดเฉพาะการเรียก UI ลงในบล็อกการจัดส่งในคิวหลัก นอกจากนี้การทำตามสิ่งที่ Apple กำหนดไว้ในแง่ของความคาดหวังเป็นเรื่องดีเสมอเนื่องจากนักพัฒนาจะคุ้นเคยกับรูปแบบนี้
Jack Lawrence

1
คำตอบที่ดี .. แต่อย่างน้อยฉันก็หวังว่าจะมีโค้ดตัวอย่างเพื่อแสดงสิ่งที่คุณกำลังพูด
Abbood

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

(สำหรับตัวอย่างที่ไม่สำคัญอย่างสมบูรณ์)
Catfish_Man

3
เป็นไปไม่ได้ในกรณีทั่วไปเนื่องจากเป็นไปได้ (และในความเป็นจริงแล้วค่อนข้างน่าจะเป็นไปได้) ที่จะอยู่ในคิวมากกว่าหนึ่งคิวพร้อมกันเนื่องจาก dispatch_sync () และ dispatch_set_target_queue () มีบางส่วนของกรณีทั่วไปที่เป็นไปได้
Catfish_Man

27

นี่เป็นแนวทางที่ไม่ถูกต้องสำหรับ API ที่คุณกำลังอธิบายถึง หาก API ยอมรับบล็อกและบล็อกที่สมบูรณ์เพื่อเรียกใช้ข้อเท็จจริงต่อไปนี้จะต้องเป็นจริง:

  1. "block to run" ควรรันบนคิวภายในเช่นคิวซึ่งเป็นส่วนตัวของ API และด้วยเหตุนี้ทั้งหมดจึงอยู่ภายใต้การควบคุมของ API นั้น ข้อยกเว้นเพียงประการเดียวคือหาก API ประกาศโดยเฉพาะว่าบล็อกจะถูกรันบนคิวหลักหรือหนึ่งในคิวที่ทำงานพร้อมกันทั่วโลก

  2. บล็อกเสร็จควรเสมอจะแสดงเป็น tuple (คิวบล็อก) เว้นแต่สมมติฐานเช่นเดียวกับ # 1 ถือเป็นจริงเช่นบล็อกเสร็จสิ้นจะมีการทำงานในคิวโลกที่รู้จักกัน นอกจากนี้บล็อกการเสร็จสมบูรณ์ควรถูกส่งแบบ async ในคิวที่ส่งผ่าน

สิ่งเหล่านี้ไม่ใช่แค่ประเด็นเกี่ยวกับโวหารเท่านั้น แต่ยังจำเป็นอย่างยิ่งหาก API ของคุณต้องปลอดภัยจากการชะงักงันหรือพฤติกรรมอื่น ๆ ที่อาจจะแขวนคุณไว้จากต้นไม้ที่ใกล้ที่สุดในสักวันหนึ่ง :-)


11
ฟังดูสมเหตุสมผล แต่ด้วยเหตุผลบางประการที่ไม่ใช่แนวทางของ Apple สำหรับ API ของตัวเอง: วิธีการส่วนใหญ่ที่ใช้การบล็อกเสร็จสิ้นก็ไม่ต้องรอคิว ...
cfischer

2
จริงและเพื่อแก้ไขการยืนยันก่อนหน้าของฉันบ้างหากเห็นได้ชัดว่าบล็อกการเสร็จสมบูรณ์จะถูกรันบนคิวหลักหรือคิวพร้อมกันทั่วโลก ฉันจะเปลี่ยนคำตอบเพื่อบ่งบอกให้มากที่สุด
jkh

หากต้องการแสดงความคิดเห็นเกี่ยวกับ Apple ที่ไม่ใช้แนวทางดังกล่าว: Apple ไม่ได้ "ถูก" เสมอไป ข้อโต้แย้งที่เหมาะสมนั้นแข็งแกร่งกว่าอำนาจใด ๆ เสมอซึ่งนักวิทยาศาสตร์ทุกคนจะยืนยัน ฉันคิดว่าคำตอบข้างต้นระบุได้เป็นอย่างดีจากมุมมองสถาปัตยกรรมซอฟต์แวร์ที่เหมาะสม
Werner Altewischer

14

คำตอบอื่น ๆ นั้นยอดเยี่ยม แต่สำหรับฉันคำตอบนั้นมีโครงสร้าง ฉันมีวิธีการเช่นนี้ใน Singleton:

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

ซึ่งมีสองการอ้างอิงซึ่ง ได้แก่ :

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

ด้วยวิธีนี้ฉันรวมศูนย์การโทรเพื่อส่งไปยังเธรดอื่น


12

คุณควรระมัดระวังเกี่ยวกับการใช้งานdispatch_get_current_queueตั้งแต่แรก จากไฟล์ส่วนหัว:

แนะนำสำหรับการดีบักและการบันทึกเท่านั้น:

โค้ดจะต้องไม่ตั้งสมมติฐานใด ๆ เกี่ยวกับคิวที่ส่งคืนเว้นแต่ว่าจะเป็นคิวส่วนกลางหรือคิวที่โค้ดได้สร้างขึ้นเอง รหัสต้องไม่ถือว่าการดำเนินการแบบซิงโครนัสไปยังคิวนั้นปลอดภัยจากการหยุดชะงักหากคิวนั้นไม่ใช่คิวที่ส่งคืนโดย dispatch_get_current_queue ()

คุณสามารถทำอย่างใดอย่างหนึ่งจากสองสิ่ง:

  1. ให้อ้างอิงถึงคิวที่คุณโพสต์ไว้ในตอนแรก (หากคุณสร้างผ่านdispatch_queue_create) และใช้ต่อจากนั้นเป็นต้นไป

  2. ใช้คิวที่ระบบกำหนดผ่านdispatch_get_global_queueและติดตามว่าคุณกำลังใช้คิวใดอยู่

อย่างมีประสิทธิภาพในขณะที่ก่อนหน้านี้อาศัยระบบเพื่อติดตามคิวที่คุณอยู่คุณจะต้องดำเนินการด้วยตัวเอง


16
เราจะ "อ้างอิงถึงคิวที่คุณโพสต์ไว้" ได้อย่างไรหากเราไม่สามารถใช้dispatch_get_current_queue()เพื่อค้นหาว่าคิวใดคืออะไร? บางครั้งโค้ดที่ต้องการทราบว่าคิวใดที่รันอยู่ก็ไม่มีการควบคุมหรือความรู้ใด ๆ ฉันมีโค้ดจำนวนมากที่สามารถ (และควร) ถูกเรียกใช้งานบนคิวพื้นหลัง แต่บางครั้งจำเป็นต้องอัปเดต gui (แถบความคืบหน้า ฯลฯ ) ดังนั้นจึงจำเป็นต้องส่ง Dispatch_sync () ไปยังคิวหลักสำหรับการดำเนินการเหล่านั้น หากอยู่ในคิวหลักแล้ว dispatch_sync () จะล็อกตลอดไป ฉันจะใช้เวลาหลายเดือนในการ refactor รหัสของฉันสำหรับสิ่งนี้
Abhi Beckert

3
ฉันคิดว่า NSURLConnection ให้การเรียกกลับที่สมบูรณ์ในเธรดเดียวกับที่เรียกจาก จะใช้ API เดียวกัน "dispatch_get_current_queue" ในการจัดเก็บคิวที่เรียกจากที่จะใช้ในเวลาที่โทรกลับหรือไม่
defactodeity

5

Apple เลิกใช้งานไปแล้วdispatch_get_current_queue()แต่ทิ้งช่องว่างไว้ที่อื่นดังนั้นเรายังสามารถรับคิวการจัดส่งปัจจุบันได้:

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

สิ่งนี้ใช้ได้กับคิวหลักเป็นอย่างน้อย หมายเหตุunderlyingQueueคุณสมบัตินั้นพร้อมใช้งานตั้งแต่ iOS 8

หากคุณจำเป็นต้องดำเนินการบล็อกการทำให้สมบูรณ์ในคิวเดิมคุณอาจใช้OperationQueueโดยตรงฉันหมายถึงไม่มี GCD



0

นี่คือคำตอบสำหรับฉัน ดังนั้นฉันจะพูดถึงกรณีการใช้งานของเรา

เรามีเลเยอร์บริการและเลเยอร์ UI (รวมถึงเลเยอร์อื่น ๆ ) ชั้นบริการจะเรียกใช้งานในพื้นหลัง (งานจัดการข้อมูลงาน CoreData การโทรเครือข่าย ฯลฯ ) ชั้นบริการมีคิวการดำเนินการสองสามคิวเพื่อตอบสนองความต้องการของเลเยอร์ UI

เลเยอร์ UI อาศัยเลเยอร์บริการเพื่อทำงานจากนั้นเรียกใช้บล็อกการทำให้สำเร็จ บล็อกนี้สามารถมีรหัส UIKit อยู่ในนั้น กรณีใช้งานง่ายๆคือรับข้อความทั้งหมดจากเซิร์ฟเวอร์และโหลดมุมมองคอลเลกชันใหม่

ที่นี่เรารับประกันว่าบล็อกที่ส่งผ่านไปยังชั้นบริการจะถูกส่งไปตามคิวที่เรียกใช้บริการ เนื่องจาก dispatch_get_current_queue เป็นวิธีที่เลิกใช้แล้วเราจึงใช้ NSOperationQueue.currentQueue เพื่อรับคิวปัจจุบันของผู้โทร หมายเหตุสำคัญเกี่ยวกับคุณสมบัตินี้

การเรียกใช้เมธอดนี้จากภายนอกบริบทของการดำเนินการที่กำลังทำงานอยู่มักจะส่งคืนค่าศูนย์

เนื่องจากเรามักเรียกใช้บริการของเราตามคิวที่ทราบ (คิวที่กำหนดเองและคิวหลัก) สิ่งนี้จึงเหมาะกับเรา เรามีกรณีที่ serviceA สามารถเรียก serviceB ซึ่งสามารถเรียก serviceC ได้ เนื่องจากเราควบคุมว่าจะให้เรียกใช้บริการครั้งแรกจากที่ใดเราจึงทราบว่าบริการที่เหลือจะปฏิบัติตามกฎเดียวกัน

ดังนั้น NSOperationQueue.currentQueue จะส่งคืนหนึ่งในคิวของเราหรือ MainQueue เสมอ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.