iOS เริ่มเธรดพื้นหลัง


117

ฉันมี sqlitedb ขนาดเล็กในอุปกรณ์ iOS ของฉัน เมื่อผู้ใช้กดปุ่มฉันจะดึงข้อมูลจาก sqlite และแสดงให้ผู้ใช้เห็น

ส่วนที่ดึงข้อมูลนี้ฉันต้องการทำในเธรดพื้นหลัง (เพื่อไม่บล็อกเธรดหลักของ UI) ฉันทำแบบนี้ -

[self performSelectorInBackground:@selector(getResultSetFromDB:) withObject:docids];

หลังจากดึงข้อมูลและประมวลผลเล็กน้อยฉันต้องอัปเดต UI แต่เนื่องจาก (ตามแนวทางปฏิบัติที่ดี) เราไม่ควรทำการอัปเดต UI จากเธรดพื้นหลัง ฉันเรียกว่าselectorบน mainthread เช่นนั้น -

[self performSelectorOnMainThread:@selector(showResults) withObject:nil waitUntilDone:NO];

แต่แอพของฉันขัดข้องในขั้นตอนแรก เช่นเริ่มต้นเธรดพื้นหลัง นี่ไม่ใช่วิธีเริ่มเธรดพื้นหลังใน iOS ใช่ไหม

อัปเดต 1:หลังจากที่[self performSelectorInBackground....ฉันได้รับ stacktrace นี้ไม่มีข้อมูลอะไรเลย -

ใส่คำอธิบายภาพที่นี่

อัปเดต 2:ฉันได้ลองเริ่มเธรดพื้นหลังเช่นนั้น - [NSThread detachNewThreadSelector:@selector(getResultSetFromDB:) toTarget:self withObject:docids];แต่ฉันก็ยังได้รับ stacktrace เหมือนเดิม

เพื่อให้ฉันชี้แจงเมื่อฉันดำเนินการนี้กับเธรดหลักทุกอย่างราบรื่น ...

อัปเดต 3นี่เป็นวิธีที่ฉันพยายามเรียกใช้จากพื้นหลัง

- (void)getResultSetFromDB:(NSMutableArray *)toProceessDocids
{
    SpotMain *mirror = [[SpotMain alloc] init];
    NSMutableArray *filteredDocids = toProceessDocids;

    if(![gMediaBucket isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForMediaBucketWithDocID:filteredDocids mBucket:gMediaBucket numRes:-1];
    if(![gMediaType isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForMediaType:filteredDocids mediaType:gMediaType numRes:-1];
    if(![gPlatform isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForPlatformID:filteredDocids platformId:@"1" numRes:-1];

    self.resultSet = [mirror FetchObjectFromDocid:filteredDocids];
    [filteredDocids release];
    [mirror release];

    [self performSelectorOnMainThread:@selector(showResults) withObject:nil waitUntilDone:NO];
    return;
}

คุณได้รับข้อผิดพลาด / บันทึกข้อขัดข้องอะไร
jtbandes

โปรดดูการอัปเดตของฉัน ...
Srikar Appalaraju

คุณช่วยแสดงวิธีการโทรในพื้นหลังได้ไหม และตรวจสอบให้แน่ใจว่าวัตถุdocidsนั้นยังคงอยู่
Rog

ใช่อยู่docids retainฉันได้วางไว้ใน.hฐานะ@property (nonatomic, retain) NSMutableArray *docids;
Srikar Appalaraju

อย่าใส่คำนำหน้าด้วยวิธีget; ที่ควรจะเป็นresultSetFromDB:
bbum

คำตอบ:


270

หากคุณใช้performSelectorInBackground:withObject:เพื่อสร้างเธรดใหม่ตัวเลือกที่ดำเนินการจะรับผิดชอบในการตั้งค่าพูลรีลีสอัตโนมัติของเธรดใหม่รันลูปและรายละเอียดการกำหนดค่าอื่น ๆ โปรดดู"การใช้ NSObject เพื่อวางไข่เธรด"ในคู่มือการเขียนโปรแกรมเธรดของ Appleคู่มือ

คุณน่าจะดีกว่าเมื่อใช้Grand Central Dispatchแม้ว่า:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self getResultSetFromDB:docids];
});

GCD เป็นเทคโนโลยีที่ใหม่กว่าและมีประสิทธิภาพมากกว่าในแง่ของหน่วยความจำเหนือศีรษะและบรรทัดของโค้ด


อัปเดตด้วยเคล็ดลับสำหรับChris Noletซึ่งเป็นผู้แนะนำการเปลี่ยนแปลงที่ทำให้โค้ดด้านบนง่ายขึ้นและสอดคล้องกับตัวอย่างรหัส GCD ล่าสุดของ Apple


เย็น! ไม่รู้เรื่องนี้ สิ่งนี้ใช้ได้กับ[NSThread detachNewThreadSelector:@selector....ด้วยหรือไม่?
Srikar Appalaraju

ใช่. ตามเอกสารของ Apple การเรียกperformSelectorInBackground:withObject:"เหมือนกับว่าคุณเรียกdetachNewThreadSelector:toTarget:withObject:วิธีการNSThreadโดยใช้วัตถุตัวเลือกและออบเจ็กต์พารามิเตอร์ในปัจจุบันเป็นพารามิเตอร์"
Scott Forbes

มีความแตกต่างระหว่าง(unsigned long)NULLและ0ในเรื่องนี้หรือไม่?
Sti

4
@Sti จาก apple Dev Docs : หมายเหตุ: อาร์กิวเมนต์ที่สองของฟังก์ชัน dispatch_get_global_queue สงวนไว้สำหรับการขยายในอนาคต ในตอนนี้คุณควรส่ง 0 สำหรับอาร์กิวเมนต์นี้เสมอ
Jawad Al Shaikh

จากนั้นฉันควรใช้ performSelectorOnMainThread เพื่ออัปเดต UI พร้อมผลการดำเนินการหรือมีวิธีที่สอดคล้องกันมากขึ้นในการอัปเดต UI กับ GCD
Ilya Denisov

9

นั่นเป็นเรื่องง่ายมากสำหรับ GCD ขั้นตอนการทำงานทั่วไปจะเป็นดังนี้:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^{
        // Perform async operation
        // Call your method/function here
        // Example:
        // NSString *result = [anObject calculateSomething];
                dispatch_sync(dispatch_get_main_queue(), ^{
                    // Update UI
                    // Example:
                    // self.myLabel.text = result;
                });
    });

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ GCD คุณสามารถดูเอกสารของ Apple ได้ที่นี่


4

เปิดใช้งานNSZombieEnabledเพื่อให้ทราบว่าวัตถุใดกำลังถูกปล่อยและเข้าถึงแล้ว จากนั้นตรวจสอบว่าgetResultSetFromDB:มีส่วนเกี่ยวข้องกับสิ่งนั้นหรือไม่ ตรวจสอบด้วยว่าdocidsมีอะไรอยู่ข้างในหรือไม่และมีการเก็บรักษาหรือไม่

วิธีนี้คุณจะมั่นใจได้ว่าไม่มีอะไรผิดปกติ


โปรดคัดลอกบรรทัดที่คุณใช้ซึ่งทำงานได้อย่างราบรื่นบนเธรดหลัก
Nicolas S

ฉันใช้สิ่งนี้จากเธรดหลักและอย่างน้อยมันก็โดนวิธีนั้นแทนที่จะหยุดทำงานทันที -[self getResultSetFromDB:docids];
Srikar Appalaraju

คุณเปิดใช้งานสิ่งที่ฉันบอกคุณหรือไม่
Nicolas S

วางเบรกพอยต์ในบรรทัดนี้: SpotMain * mirror = [[SpotMain จัดสรร] init]; และบอกฉันว่ามันโดนหรือไม่และถ้ามันเส้นไหนขัดข้อง โปรดเปิดใช้งานซอมบี้เพื่อให้เราได้รับบันทึกข้อผิดพลาดที่ชัดเจน
Nicolas S

ใช่ฉันเปิดใช้งานซอมบี้แล้ว ฉันได้รับสิ่งนี้ - `2011-08-14 12: 49: 42.697 FLO [16211: 707] *** - [FMResultSet release]: ข้อความที่ส่งไปยัง deallocated instance 0x2bff80 2011-08-14 12: 49: 42.697 FLO [16211: 1607] *** __NSAutoreleaseNoPool (): Object 0x2c0cc0 ของคลาส __NSCFData autoreleased โดยไม่มีพูลอยู่ในตำแหน่ง - เพิ่งรั่ว. Also when I try to call this method from background thread I do not reach กระจก SpotMain * ... `มันขัดข้องทันทีหลังจากเข้าสู่เธรดพื้นหลัง ...
Srikar Appalaraju

2

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


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