วิธีที่ดีที่สุดที่จะลบออกจาก NSMutableArray ขณะทำซ้ำ?


194

ในโกโก้ถ้าฉันต้องการวนลูปผ่าน NSMutableArray และลบวัตถุหลายอย่างที่ตรงกับเกณฑ์ที่กำหนดวิธีที่ดีที่สุดในการทำเช่นนี้คืออะไรโดยไม่ต้องรีสตาร์ทลูปทุกครั้งที่ฉันลบวัตถุออก

ขอบคุณ

แก้ไข: เพียงเพื่อชี้แจง - ฉันกำลังมองหาวิธีที่ดีที่สุดเช่นบางสิ่งที่สวยงามกว่าการอัปเดตดัชนีด้วยตนเองที่ฉันอยู่ เช่นใน C ++ ฉันสามารถทำได้

iterator it = someList.begin();

while (it != someList.end())
{
    if (shouldRemove(it))   
        it = someList.erase(it);
}

9
วนจากด้านหลังไปด้านหน้า
Licks ร้อน

ไม่มีใครตอบ "WHY"
onmyway133

1
@HotLicks หนึ่งในรายการโปรดตลอดเวลาของฉันและวิธีการแก้ปัญหาที่ประเมินต่ำที่สุดในการเขียนโปรแกรมโดยทั่วไป: D
Julian F. Weinert

คำตอบ:


388

เพื่อความชัดเจนฉันต้องการวนรอบเริ่มต้นที่ฉันรวบรวมรายการเพื่อลบ แล้วฉันจะลบพวกเขา นี่คือตัวอย่างการใช้ Objective-C 2.0

NSMutableArray *discardedItems = [NSMutableArray array];

for (SomeObjectClass *item in originalArrayOfItems) {
    if ([item shouldBeDiscarded])
        [discardedItems addObject:item];
}

[originalArrayOfItems removeObjectsInArray:discardedItems];

ดังนั้นจึงไม่มีคำถามว่าดัชนีมีการปรับปรุงอย่างถูกต้องหรือไม่หรือรายละเอียดการทำบัญชีอื่น ๆ

แก้ไขเพื่อเพิ่ม:

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

สูตรผกผันอาจเร็วขึ้น แต่ฉันไม่เคยต้องการที่จะใส่ใจว่าเป็นเพราะสูตรข้างต้นนั้นเร็วพอสำหรับความต้องการของฉัน

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


47
ระวังว่าสิ่งนี้สามารถสร้างข้อบกพร่องหากวัตถุมากกว่าหนึ่งครั้งในอาร์เรย์ คุณสามารถใช้ NSMutableIndexSet และ - (void) removeObjectsAtIndexes
Georg Schölly

1
มันเร็วกว่าที่จะทำสิ่งที่ตรงกันข้าม ความแตกต่างด้านประสิทธิภาพนั้นค่อนข้างใหญ่ แต่ถ้าอาร์เรย์ของคุณไม่ใหญ่มาก
user1032657

ฉันใช้ reverse ... สร้างอาร์เรย์เป็นศูนย์ .. จากนั้นเพิ่มเฉพาะสิ่งที่ฉันต้องการและโหลดข้อมูล ... เสร็จแล้ว ... สร้าง dummyArray ซึ่งเป็นมิเรอร์ของอาร์เรย์หลักเพื่อให้เรามีข้อมูลจริงที่สำคัญ
Fahim Parkar

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

82

อีกรูปแบบหนึ่ง ดังนั้นคุณจะสามารถอ่านและแสดงได้ดี:

NSMutableIndexSet *discardedItems = [NSMutableIndexSet indexSet];
SomeObjectClass *item;
NSUInteger index = 0;

for (item in originalArrayOfItems) {
    if ([item shouldBeDiscarded])
        [discardedItems addIndex:index];
    index++;
}

[originalArrayOfItems removeObjectsAtIndexes:discardedItems];

นี่มันเจ๋งมาก. ฉันพยายามลบหลายรายการออกจากอาร์เรย์และสิ่งนี้ใช้ได้อย่างสมบูรณ์ วิธีการอื่นก่อให้เกิดปัญหา :) คนขอบคุณ
AbhinavVinay

คอเรย์คำตอบนี้ (ด้านล่าง) กล่าวว่าremoveObjectsAtIndexesเป็นวิธีที่เลวร้ายที่สุดในการลบวัตถุคุณเห็นด้วยหรือไม่? ฉันถามคำถามนี้เพราะคำตอบของคุณเก่าเกินไป ยังคงเป็นการดีที่จะเลือกสิ่งที่ดีที่สุด?
Hemang

ฉันชอบวิธีนี้มากในแง่ของการอ่าน ในแง่ของประสิทธิภาพเมื่อเปรียบเทียบกับ @HotLicks ตอบอย่างไร
Victor Maia Aldecôa

วิธีนี้ควรได้รับการสนับสนุนอย่างแน่นอนในขณะที่การลบวัตถุออกจากอาร์เรย์ใน Obj-C
2559

enumerateObjectsUsingBlock:จะช่วยให้คุณเพิ่มดัชนีได้ฟรี
pkamb

42

นี่เป็นปัญหาที่ง่ายมาก คุณทำซ้ำย้อนหลัง:

for (NSInteger i = array.count - 1; i >= 0; i--) {
   ElementType* element = array[i];
   if ([element shouldBeRemoved]) {
       [array removeObjectAtIndex:i];
   }
}

นี่เป็นรูปแบบที่ธรรมดามาก


จะพลาดคำตอบของ Jens เพราะเขาไม่ได้เขียนรหัส: ขอบคุณ .. m8
FlowUI SimpleUITesting.com

3
นี่เป็นวิธีที่ดีที่สุด สิ่งสำคัญที่ต้องจำไว้ว่าตัวแปรการวนซ้ำนั้นควรเป็นตัวแปรที่มีการลงนามแล้วแม้ว่าดัชนีอาร์เรย์ใน Objective-C นั้นจะถูกประกาศว่าไม่ได้ลงนาม (ฉันสามารถจินตนาการได้ว่า Apple เสียใจที่ตอนนี้)
mojuba

39

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

มีประสิทธิภาพมากขึ้นจะเป็นดังต่อไปนี้:

NSMutableArray *array = ...
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
    if (! shouldRemove(object)) {
        [itemsToKeep addObject:object];
    }
}
[array setArray:itemsToKeep];

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

[array release];
array = [itemsToKeep retain];

ดังนั้นไม่จำเป็นต้องคัดลอกค่าเพียงสลับตัวชี้เท่านั้น


อัลโกนี้พิสูจน์ได้ว่าเป็นเรื่องที่ซับซ้อนในแง่ของเวลา ฉันพยายามที่จะเข้าใจในแง่ของความซับซ้อนของพื้นที่ ฉันมีการใช้งานแบบเดียวกันกับที่ฉันตั้งค่าความสามารถสำหรับ 'itemsToKeep' ด้วย 'Array count' ที่แท้จริง แต่บอกว่าฉันไม่ต้องการวัตถุสองสามอย่างจากอาร์เรย์ดังนั้นฉันจึงไม่เพิ่มลงใน itemsToKeep ดังนั้นความสามารถของไอเท็มของฉัน ToKeep คือ 10 แต่จริง ๆ แล้วฉันเก็บวัตถุเพียง 6 ชิ้นเท่านั้น นี่หมายความว่าฉันกำลังสูญเสียพื้นที่ของวัตถุ 4 หรือไม่? PS ไม่พบข้อผิดพลาดเพียงพยายามเข้าใจความซับซ้อนของอัลโก :)
tech_human

ใช่คุณมีพื้นที่สงวนไว้สำหรับตัวชี้สี่ตัวที่คุณไม่ได้ใช้งาน โปรดทราบว่าอาเรย์นั้นมีเพียงพอยน์เตอร์เท่านั้นไม่ใช่ตัววัตถุดังนั้นสำหรับสี่วัตถุที่หมายถึง 16 ไบต์ (สถาปัตยกรรม 32 บิต) หรือ 32 ไบต์ (64 บิต)
benzado

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

โอเคเข้าใจแล้ว ขอบคุณ Benzado !!
tech_human

ตามที่โพสต์นี้คำแนะนำของวิธีการ arrayWithCapacity: เกี่ยวกับขนาดของอาร์เรย์ที่ไม่ได้ใช้งานจริง
Kremk

28

คุณสามารถใช้ NSpredicate เพื่อลบรายการออกจากอาเรย์ที่ไม่แน่นอนของคุณ สิ่งนี้ไม่ต้องการลูป

ตัวอย่างเช่นหากคุณมี NSMutableArray ชื่อคุณสามารถสร้างเพรดิเคตแบบนี้ได้:

NSPredicate *caseInsensitiveBNames = 
[NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];

บรรทัดต่อไปนี้จะทำให้คุณมีอาร์เรย์ที่มีเฉพาะชื่อที่ขึ้นต้นด้วย b

[namesArray filterUsingPredicate:caseInsensitiveBNames];

หากคุณมีปัญหาในการสร้างภาคแสดงที่คุณต้องการให้ใช้ลิงค์นักพัฒนาแอปเปิ้ลนี้


3
ไม่ต้องมองเห็นลูป มีการวนซ้ำในการดำเนินการตัวกรองคุณแค่ไม่เห็น
Hot Licks

18

ฉันทำการทดสอบประสิทธิภาพโดยใช้ 4 วิธีที่แตกต่างกัน การทดสอบแต่ละครั้งจะทำซ้ำผ่านองค์ประกอบทั้งหมดในอาร์เรย์องค์ประกอบ 100,000 รายการและลบทุกรายการที่ 5 ผลลัพธ์ไม่ได้แตกต่างกันมากนักเมื่อมี / ไม่มีการปรับให้เหมาะสม สิ่งเหล่านี้ทำบน iPad 4:

(1) removeObjectAtIndex:- 271 ms

(2) removeObjectsAtIndexes:- 1010 ms (เพราะการสร้างชุดดัชนีใช้เวลา ~ 700 ms มิฉะนั้นนี่จะเป็นแบบเดียวกับการเรียก removeObjectAtIndex: สำหรับแต่ละรายการ)

(3) removeObjects:- 326 ms

(4) สร้างอาร์เรย์ใหม่ด้วยวัตถุที่ผ่านการทดสอบ - 17 ms

ดังนั้นการสร้างอาร์เรย์ใหม่จึงเร็วที่สุด วิธีการอื่น ๆ นั้นเทียบเคียงได้ยกเว้นว่าการใช้ removeObjectsAtIndexes: จะแย่กว่าเมื่อมีรายการที่ต้องลบเพิ่มเติมเนื่องจากเวลาที่ต้องใช้ในการสร้างชุดดัชนี


1
คุณนับเวลาที่จะสร้างอาร์เรย์ใหม่และยกเลิกการจัดสรรในภายหลัง ฉันไม่เชื่อว่าสามารถทำได้เพียงอย่างเดียวใน 17 มิลลิวินาที บวก 75,000 งาน?
แจ็ค

เวลาที่ใช้ในการสร้างและจัดสรรคืนอาเรย์ใหม่นั้นน้อยมาก
user1032657

เห็นได้ชัดว่าคุณไม่ได้วัดรูปแบบลูปย้อนกลับ
Hot Licks

การทดสอบครั้งแรกนั้นเทียบเท่ากับการวนลูปย้อนกลับ
user1032657

จะเกิดอะไรขึ้นเมื่อคุณถูกทิ้งให้อยู่กับอาร์เรย์ใหม่ของคุณและอาร์เรย์ของคุณจะว่างเปล่า คุณจะต้องคัดลอก newArray ไปยังสิ่งที่ชื่อ PrevArray เป็น (หรือเปลี่ยนชื่อ) มิฉะนั้นรหัสอื่น ๆ ของคุณจะอ้างอิงได้อย่างไร?
aremvee

17

ใช้การวนซ้ำนับลงในดัชนี:

for (NSInteger i = array.count - 1; i >= 0; --i) {

หรือทำสำเนากับวัตถุที่คุณต้องการเก็บ

โดยเฉพาะอย่างยิ่งไม่ได้ใช้ห่วงหรือfor (id object in array)NSEnumerator


3
คุณควรเขียนคำตอบของคุณในรหัส m8 ฮ่าฮ่าเกือบพลาด
FlowUI SimpleUITesting.com

นี้ไม่ถูกต้อง. "สำหรับ (id วัตถุในอาร์เรย์)" เร็วกว่า "สำหรับ (NSInteger i = array.count - 1; i> = 0; --i)" และเรียกว่าการวนซ้ำอย่างรวดเร็ว การใช้ตัววนซ้ำนั้นเร็วกว่าการทำดัชนีอย่างแน่นอน
แจ็ค

@jack - แต่ถ้าคุณลบองค์ประกอบออกจากตัววนซ้ำคุณมักจะสร้างความเสียหาย (และ "การทำซ้ำอย่างรวดเร็ว" ไม่ได้เร็วเท่านี้เสมอไป)
Hot Licks

คำตอบคือจากปี 2008 ไม่เกิดซ้ำอย่างรวดเร็ว
Jens Ayton

12

สำหรับ iOS 4+ หรือ OS X 10.6+ แอปเปิ้ลได้เพิ่มpassingTestAPIs เข้าNSMutableArrayด้วย– indexesOfObjectsPassingTest:กัน โซลูชันที่มี API ดังกล่าวจะเป็น:

NSIndexSet *indexesToBeRemoved = [someList indexesOfObjectsPassingTest:
    ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self shouldRemove:obj];
}];
[someList removeObjectsAtIndexes:indexesToBeRemoved];

12

ทุกวันนี้คุณสามารถใช้การแจงนับแบบบล็อกย้อนกลับได้ รหัสตัวอย่างง่าย ๆ :

NSMutableArray *array = [@[@{@"name": @"a", @"shouldDelete": @(YES)},
                           @{@"name": @"b", @"shouldDelete": @(NO)},
                           @{@"name": @"c", @"shouldDelete": @(YES)},
                           @{@"name": @"d", @"shouldDelete": @(NO)}] mutableCopy];

[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if([obj[@"shouldDelete"] boolValue])
        [array removeObjectAtIndex:idx];
}];

ผลลัพธ์:

(
    {
        name = b;
        shouldDelete = 0;
    },
    {
        name = d;
        shouldDelete = 0;
    }
)

ตัวเลือกอื่นที่มีรหัสบรรทัดเดียว:

[array filterUsingPredicate:[NSPredicate predicateWithFormat:@"shouldDelete == NO"]];

มีการรับประกันใด ๆ หรือไม่ว่าอาเรย์จะไม่ถูกจัดสรรใหม่?
Cfr

คุณหมายถึงอะไรกับการจัดสรรคืน
vikingosegundo

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

เนื่องจากการดำเนินการลบไม่ทราบว่าจะลบองค์ประกอบจำนวนเท่าใดในตอนท้ายมันจึงไม่สมเหตุสมผลในระหว่างการลบ อย่างไรก็ตาม: รายละเอียดการใช้งานต้องเชื่อใจ apple ให้เขียนรหัสที่สมเหตุสมผล
vikingosegundo

อันดับแรกไม่ได้ดีกว่า (เพราะคุณต้องคิดมากเกี่ยวกับวิธีการทำงานในตอนแรก) และอันดับที่สองไม่สามารถปรับโครงสร้างให้ปลอดภัยได้ ดังนั้นในที่สุดฉันก็จบลงด้วยวิธีที่ได้รับการยอมรับแบบคลาสสิกที่อ่านได้จริง
Renetik

8

ในทางที่ประกาศมากขึ้นขึ้นอยู่กับเกณฑ์ที่ตรงกับรายการที่จะลบคุณสามารถใช้:

[theArray filterUsingPredicate:aPredicate]

@ นาธานควรมีประสิทธิภาพมาก


6

นี่เป็นวิธีที่ง่ายและสะอาด ฉันต้องการทำซ้ำอาร์เรย์ของฉันในการเรียกใช้การแจงนับอย่างรวดเร็ว:

for (LineItem *item in [NSArray arrayWithArray:self.lineItems]) 
{
    if ([item.toBeRemoved boolValue] == YES) 
    {
        [self.lineItems removeObject:item];
    }
}

วิธีนี้คุณจะแจกแจงถึงสำเนาของอาเรย์ที่จะถูกลบออกจากทั้งสองถือวัตถุเดียวกัน NSArray ถือตัวชี้วัตถุเท่านั้นดังนั้นนี่คือหน่วยความจำ / ประสิทธิภาพที่ดีโดยสิ้นเชิง


2
หรือเรียบง่ายกว่า -for (LineItem *item in self.lineItems.copy)
Alexandre G

5

เพิ่มวัตถุที่คุณต้องการลบไปยังอาร์เรย์ที่สองและหลังจากวนซ้ำให้ใช้ -removeObjectsInArray:


5

สิ่งนี้ควรทำ:

    NSMutableArray* myArray = ....;

    int i;
    for(i=0; i<[myArray count]; i++) {
        id element = [myArray objectAtIndex:i];
        if(element == ...) {
            [myArray removeObjectAtIndex:i];
            i--;
        }
    }

หวังว่านี่จะช่วย ...


แม้ว่านอกรีตฉันได้พบซ้ำไปข้างหลังและลบขณะที่ฉันไปเป็นวิธีที่สะอาดและเรียบง่าย มักจะเป็นหนึ่งในวิธีที่เร็วที่สุดเช่นกัน
rpetrich

เกิดอะไรขึ้นกับสิ่งนี้? มันสะอาดเร็วอ่านง่ายและใช้งานได้เหมือนมีเสน่ห์ สำหรับฉันมันดูเหมือนคำตอบที่ดีที่สุด ทำไมถึงมีคะแนนติดลบ ฉันทำอะไรบางอย่างหายไปหรือเปล่า
Steph Thirion

1
@Steph: คำถามระบุ "สิ่งที่สวยงามกว่าการอัปเดตดัชนีด้วยตนเอง"
Steve Madsen

1
โอ้ ฉันพลาดส่วนที่ฉันไม่ต้องการอัพเดทด้วยตนเอง ขอบคุณสตีฟ IMO โซลูชันนี้มีความหรูหรามากกว่าที่เลือกไว้ (ไม่จำเป็นต้องมีอาร์เรย์ชั่วคราว) ดังนั้นคะแนนโหวตติดลบจึงไม่เป็นธรรม
Steph Thirion

@Steve: หากคุณตรวจสอบการแก้ไขส่วนนั้นจะถูกเพิ่มหลังจากที่ฉันได้ส่งคำตอบของฉัน .... ถ้าไม่ฉันจะได้ตอบกลับด้วย "Iterating ถอยหลังเป็นทางออกที่ดีที่สุด" :) ขอให้มีความสุขมาก ๆ ในวันนี้!
Pokot0

1

ทำไมคุณไม่เพิ่มวัตถุที่จะถูกลบไปที่ NSMutableArray อื่น เมื่อคุณทำซ้ำเสร็จแล้วคุณสามารถลบวัตถุที่คุณรวบรวมได้


1

วิธีการแลกเปลี่ยนองค์ประกอบที่คุณต้องการลบด้วยองค์ประกอบ 'n'th,' n-1'th องค์ประกอบและอื่น ๆ

เมื่อเสร็จแล้วคุณจะต้องปรับขนาดอาร์เรย์เป็น 'ขนาดก่อนหน้า - จำนวนการสลับ'


1

หากวัตถุทั้งหมดในอาเรย์ของคุณไม่ซ้ำกันหรือคุณต้องการลบสิ่งที่เกิดขึ้นทั้งหมดเมื่อพบคุณสามารถระบุจำนวนอย่างรวดเร็วในอาเรย์สำเนาและใช้ [NSMutableArray removeObject:] เพื่อลบออบเจ็กต์จากต้นฉบับ

NSMutableArray *myArray;
NSArray *myArrayCopy = [NSArray arrayWithArray:myArray];

for (NSObject *anObject in myArrayCopy) {
    if (shouldRemove(anObject)) {
        [myArray removeObject:anObject];
    }
}

จะเกิดอะไรขึ้นหากmyArrayดั้งเดิมได้รับการอัพเดตในขณะที่+arrayWithArrayกำลังดำเนินการ
bioffe

1
@bioffe: แล้วคุณมีข้อบกพร่องในรหัสของคุณ NSMutableArray ไม่ปลอดภัยสำหรับเธรดคุณคาดว่าจะควบคุมการเข้าถึงผ่านการล็อก ดูคำตอบนี้
dreamlax

1

ผู้ประกาศข่าวของ benzado ข้างต้นคือสิ่งที่คุณควรทำเพื่อ preformace ในหนึ่งในแอปพลิเคชันของฉัน removeObjectsInArray ใช้เวลาทำงาน 1 นาทีเพียงเพิ่มอาร์เรย์ใหม่ใช้เวลา 0.23 วินาที


1

ฉันกำหนดหมวดหมู่ที่ให้ฉันกรองโดยใช้บล็อกเช่นนี้

@implementation NSMutableArray (Filtering)

- (void)filterUsingTest:(BOOL (^)(id obj, NSUInteger idx))predicate {
    NSMutableIndexSet *indexesFailingTest = [[NSMutableIndexSet alloc] init];

    NSUInteger index = 0;
    for (id object in self) {
        if (!predicate(object, index)) {
            [indexesFailingTest addIndex:index];
        }
        ++index;
    }
    [self removeObjectsAtIndexes:indexesFailingTest];

    [indexesFailingTest release];
}

@end

ซึ่งสามารถใช้เช่นนี้:

[myMutableArray filterUsingTest:^BOOL(id obj, NSUInteger idx) {
    return [self doIWantToKeepThisObject:obj atIndex:idx];
}];

1

การปรับใช้ nicer สามารถใช้วิธีการประเภทด้านล่างใน NSMutableArray

@implementation NSMutableArray(BMCommons)

- (void)removeObjectsWithPredicate:(BOOL (^)(id obj))predicate {
    if (predicate != nil) {
        NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:self.count];
        for (id obj in self) {
            BOOL shouldRemove = predicate(obj);
            if (!shouldRemove) {
                [newArray addObject:obj];
            }
        }
        [self setArray:newArray];
    }
}

@end

บล็อกภาคแสดงสามารถนำมาใช้เพื่อทำการประมวลผลบนแต่ละวัตถุในอาร์เรย์ ถ้าเพรดิเคตส่งคืนจริงวัตถุจะถูกลบออก

ตัวอย่างสำหรับอาร์เรย์วันที่ที่จะลบวันที่ทั้งหมดที่อยู่ในอดีต:

NSMutableArray *dates = ...;
[dates removeObjectsWithPredicate:^BOOL(id obj) {
    NSDate *date = (NSDate *)obj;
    return [date timeIntervalSinceNow] < 0;
}];

0

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

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

ภายใต้ Xcode 6 งานนี้

NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];

    for (id object in array)
    {
        if ( [object isNotEqualTo:@"whatever"]) {
           [itemsToKeep addObject:object ];
        }
    }
    array = nil;
    array = [[NSMutableArray alloc]initWithArray:itemsToKeep];
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.