วิธีที่ดีที่สุดในการวางโครงสร้าง c ใน NSArray คืออะไร?


89

วิธีปกติในการจัดเก็บโครงสร้าง c ใน an NSArrayคืออะไร? ข้อดีข้อเสียการจัดการหน่วยความจำ?

โดยเฉพาะอย่างยิ่งความแตกต่างระหว่างvalueWithBytesและvalueWithPointer - เลี้ยงโดยจัสตินกับปลาดุกด้านล่างคืออะไร

นี่คือลิงค์ไปยังการสนทนาของ Apple valueWithBytes:objCType:สำหรับผู้อ่านในอนาคต ...

สำหรับการคิดด้านข้างและดูประสิทธิภาพมากขึ้น Evgen ได้ยกประเด็นการใช้งาน STL::vectorในC ++

(นั่นทำให้เกิดประเด็นที่น่าสนใจคือมีไลบรารี c แบบเร็วซึ่งไม่ต่างจากSTL::vectorแต่เบากว่ามากที่ช่วยให้ "จัดการอาร์เรย์เป็นระเบียบเรียบร้อยน้อยที่สุด ... ?)

ดังนั้นคำถามเดิม ...

ตัวอย่างเช่น:

typedef struct _Megapoint {
    float   w,x,y,z;
} Megapoint;

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

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

BTW นี่คือแนวทาง NSData ซึ่งอาจเป็น? ไม่ดีที่สุด ...

Megapoint p;
NSArray *a = [NSArray arrayWithObjects:
    [NSData dataWithBytes:&p length:sizeof(Megapoint)],
    [NSData dataWithBytes:&p length:sizeof(Megapoint)],
    [NSData dataWithBytes:&p length:sizeof(Megapoint)],
        nil];

BTW เป็นจุดอ้างอิงและต้องขอบคุณ Jarret Hardie นี่คือวิธีการจัดเก็บCGPointsและสิ่งที่คล้ายกันในNSArray:

NSArray *points = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
        [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
        nil];

(ดูฉันจะเพิ่มวัตถุ CGPoint ไปยัง NSArray ด้วยวิธีง่ายๆได้อย่างไร )


รหัสของคุณสำหรับการแปลงเป็น NSData น่าจะใช้ได้ .. และไม่มีการรั่วไหลของหน่วยความจำ .... อย่างไรก็ตามอาจมีคนใช้อาร์เรย์ C ++ มาตรฐานของ Structs Megapoint p [3] ก็ได้เช่นกัน
Swapnil Luktuke

คุณไม่สามารถเพิ่มรางวัลได้จนกว่าคำถามจะมีอายุสองวัน
Matthew Frederick

1
valueWithCGPoint ไม่พร้อมใช้งานสำหรับ OSX แม้ว่า เป็นส่วนหนึ่งของ UIKit
lppier

@Ippier valueWithPoint พร้อมใช้งานบน OS X
Schpaencoder

คำตอบ:


160

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

เพียงใช้นิพจน์ดังต่อไปนี้:

[NSValue valueWithBytes:&p objCType:@encode(Megapoint)];

และเพื่อให้ได้มูลค่ากลับออกมา:

Megapoint p;
[value getValue:&p];

4
@ Joe Blow @Catfish_Man มันคัดลอกโครงสร้างจริงๆpไม่ใช่ตัวชี้ไปที่มัน @encodeสั่งให้ข้อมูลทั้งหมดที่จำเป็นเกี่ยวกับวิธีการใหญ่โครงสร้างเป็น เมื่อคุณปล่อยNSValue(หรือเมื่ออาร์เรย์ทำ) สำเนาของโครงสร้างจะถูกทำลาย หากคุณเคยใช้getValue:ในระหว่างนี้คุณก็สบายดี ดูส่วน "การใช้ค่า" ของ "Number and Value Programming Topics": developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…
Justin Spahr-Summers

1
@Joe Blow ถูกต้องที่สุดยกเว้นว่าจะไม่สามารถเปลี่ยนได้ในขณะรันไทม์ คุณกำลังระบุประเภท C ซึ่งจะต้องเป็นที่รู้จักเสมอ หากสามารถทำให้ "ใหญ่ขึ้น" ได้โดยการอ้างอิงข้อมูลมากขึ้นคุณอาจใช้ตัวชี้นั้นและ@encodeจะอธิบายโครงสร้างด้วยตัวชี้นั้น แต่ไม่สามารถอธิบายข้อมูลที่ชี้ไปได้ทั้งหมดซึ่งอาจเปลี่ยนแปลงได้อย่างแท้จริง
Justin Spahr-Summers

1
จะNSValueทำให้หน่วยความจำของโครงสร้างว่างโดยอัตโนมัติเมื่อถูกยกเลิกการจัดสรรหรือไม่ เอกสารประกอบไม่ชัดเจนเล็กน้อยเกี่ยวกับเรื่องนี้
devios1

1
ดังนั้นเพื่อความชัดเจนอย่างสมบูรณ์NSValueเจ้าของข้อมูลที่คัดลอกลงในตัวเองและฉันไม่ต้องกังวลเกี่ยวกับการปลดปล่อย (ภายใต้ ARC)?
devios1

1
@devios ถูกต้อง. NSValueไม่ได้ทำ "การจัดการหน่วยความจำ" แต่อย่างใดคุณสามารถคิดว่ามันเป็นเพียงแค่การมีสำเนาของค่าโครงสร้างภายใน ตัวอย่างเช่นหากโครงสร้างมีพอยน์เตอร์ซ้อนกันNSValueไม่ทราบว่าจะว่างหรือคัดลอกหรือทำอะไรกับสิ่งเหล่านั้น - มันจะปล่อยให้มันถูกแตะต้องโดยคัดลอกที่อยู่ตามที่เป็นอยู่
Justin Spahr-Summers

7

ฉันขอแนะนำให้คุณยึดติดกับNSValueเส้นทาง แต่ถ้าคุณต้องการจัดเก็บstructประเภทข้อมูลธรรมดาใน NSArray ของคุณ (และวัตถุคอลเลกชันอื่น ๆ ใน Cocoa) คุณสามารถทำได้แม้ว่าจะเป็นทางอ้อมโดยใช้ Core Foundation และการเชื่อมต่อแบบไม่เสียค่าบริการ .

CFArrayRef(และคู่กันที่ไม่แน่นอนCFMutableArrayRef) ทำให้นักพัฒนามีความยืดหยุ่นมากขึ้นเมื่อสร้างวัตถุอาร์เรย์ ดูอาร์กิวเมนต์ที่สี่ของผู้เริ่มต้นที่กำหนด:

CFArrayRef CFArrayCreate (
    CFAllocatorRef allocator,
    const void **values,
    CFIndex numValues,
    const CFArrayCallBacks *callBacks
);

สิ่งนี้ช่วยให้คุณสามารถขอให้CFArrayRefวัตถุใช้รูทีนการจัดการหน่วยความจำของ Core Foundation ไม่ได้เลยหรือแม้แต่ของคุณเองการจัดการหน่วยความจำ

ตัวอย่างบังคับ:

// One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types.
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
NSMutableArray *array = (NSMutableArray *)arrayRef;

struct {int member;} myStruct = {.member = 42};
// Casting to "id" to avoid compiler warning
[array addObject:(id)&myStruct];

// Hurray!
struct {int member;} *mySameStruct = [array objectAtIndex:0];

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

ฉันไม่อยากแนะนำวิธีแก้ปัญหานี้ แต่จะเก็บไว้ที่นี่เผื่อว่าใครจะสนใจ :-)


การใช้โครงสร้างของคุณตามที่จัดสรรไว้บนฮีป (แทนสแตก) แสดงให้เห็นที่นี่:

typedef struct {
    float w, x, y, z;
} Megapoint;

// One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types.
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
NSMutableArray *array = (NSMutableArray *)arrayRef;

Megapoint *myPoint = malloc(sizeof(Megapoint);
myPoint->w = 42.0f;
// set ivars as desired..

// Casting to "id" to avoid compiler warning
[array addObject:(id)myPoint];

// Hurray!
Megapoint *mySamePoint = [array objectAtIndex:0];

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

@ Joe Blow: นั่นเป็นจุดที่ยอดเยี่ยมที่คุณทำเกี่ยวกับการจัดการหน่วยความจำ คุณมีสิทธิ์ที่จะสับสน: ตัวอย่างโค้ดที่ฉันโพสต์ไว้ข้างต้นอาจทำให้เกิดข้อขัดข้องอย่างลึกลับขึ้นอยู่กับเวลาที่สแต็กของฟังก์ชันถูกเขียนทับ ฉันเริ่มอธิบายรายละเอียดเกี่ยวกับวิธีการใช้โซลูชันของฉัน แต่ตระหนักว่าฉันกำลังนำการนับอ้างอิงของ Objective-C มาใช้ใหม่ ฉันขอโทษสำหรับรหัสขนาดกะทัดรัด - ไม่ใช่เรื่องถนัด แต่เป็นความเกียจคร้าน ไม่มีประเด็นในการเขียนโค้ดที่คนอื่นอ่านไม่ออก :)
Sedate Alien

หากคุณพอใจที่จะ "รั่ว" (เพราะต้องการคำที่ดีกว่า) structคุณสามารถจัดสรรได้ครั้งเดียวและไม่ต้องเสียค่าใช้จ่ายในอนาคต ฉันได้รวมตัวอย่างนี้ไว้ในคำตอบที่แก้ไขแล้ว นอกจากนี้ไม่ใช่การพิมพ์ผิดmyStructเนื่องจากเป็นโครงสร้างที่จัดสรรบนสแต็กแตกต่างจากตัวชี้ไปยังโครงสร้างที่จัดสรรบนฮีป
Sedate Alien

4

วิธีการที่คล้ายกันในการเพิ่มโครงสร้าง c คือการจัดเก็บตัวชี้และการยกเลิกการอ้างอิงตัวชี้ดังนั้น

typedef struct BSTNode
{
    int data;
    struct BSTNode *leftNode;
    struct BSTNode *rightNode;
}BSTNode;

BSTNode *rootNode;

//declaring a NSMutableArray
@property(nonatomic)NSMutableArray *queues;

//storing the pointer in the array
[self.queues addObject:[NSValue value:&rootNode withObjCType:@encode(BSTNode*)]];

//getting the value
BSTNode *frontNode =[[self.queues objectAtIndex:0] pointerValue];

3

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

ฉันจะสมัครที่นี่ได้อย่างไร

คุณสามารถเรียก class_addIvar และเพิ่มตัวแปรอินสแตนซ์ Megapoint ให้กับคลาสใหม่หรือคุณสามารถสังเคราะห์ตัวแปร objc ของคลาส Megapoint ที่รันไทม์ (เช่นตัวแปรอินสแตนซ์สำหรับแต่ละฟิลด์ของ Megapoint)

อดีตเทียบเท่ากับคลาส objc ที่คอมไพล์แล้ว:

@interface MONMegapoint { Megapoint megapoint; } @end

หลังเทียบเท่ากับคลาส objc ที่คอมไพล์แล้ว:

@interface MONMegapoint { float w,x,y,z; } @end

หลังจากที่คุณเพิ่ม ivars คุณสามารถเพิ่ม / สังเคราะห์วิธีการ

ในการอ่านค่าที่เก็บไว้ในปลายทางการรับใช้วิธีการสังเคราะห์ของคุณobject_getInstanceVariableหรือvalueForKey: (ซึ่งมักจะแปลงตัวแปรอินสแตนซ์สเกลาร์เหล่านี้เป็น NSNumber หรือ NSValue แทน)

btw: คำตอบทั้งหมดที่คุณได้รับมีประโยชน์บางคำตอบดีกว่า / แย่กว่า / ไม่ถูกต้องขึ้นอยู่กับบริบท / สถานการณ์ ความต้องการเฉพาะเกี่ยวกับหน่วยความจำความเร็วความง่ายในการดูแลรักษาความสะดวกในการถ่ายโอนหรือเก็บถาวร ฯลฯ จะเป็นตัวกำหนดว่าสิ่งใดดีที่สุดสำหรับกรณีที่กำหนด ... แต่ไม่มีโซลูชันที่ 'สมบูรณ์แบบ' ที่เหมาะสำหรับทุกเรื่อง ไม่มี 'วิธีที่ดีที่สุดในการวางโครงสร้าง c ใน NSArray' เพียงแค่ 'วิธีที่ดีที่สุดในการวางโครงสร้าง c ใน NSArray สำหรับสถานการณ์กรณีหรือชุดความต้องการเฉพาะ ' ซึ่งคุณจะมี เพื่อระบุ

นอกจากนี้ NSArray เป็นอินเทอร์เฟซอาร์เรย์ที่ใช้ซ้ำได้โดยทั่วไปสำหรับชนิดขนาดตัวชี้ (หรือเล็กกว่า) แต่มีคอนเทนเนอร์อื่น ๆ ที่เหมาะสำหรับโครงสร้าง c ด้วยเหตุผลหลายประการ (std :: vector เป็นตัวเลือกทั่วไปสำหรับ c-structs)


ภูมิหลังของผู้คนเข้ามามีบทบาทเช่นกัน ... การที่คุณต้องใช้โครงสร้างนั้นมักจะขจัดความเป็นไปได้บางอย่าง 4 โฟลตนั้นค่อนข้างจะเข้าใจผิดได้ แต่รูปแบบโครงสร้างแตกต่างกันไปตามสถาปัตยกรรม / คอมไพเลอร์มากเกินไปที่จะใช้การแสดงหน่วยความจำที่ต่อเนื่องกัน (เช่น NSData) และคาดว่าจะใช้งานได้ เครื่องอนุกรม objc ของชายผู้น่าสงสารน่าจะมีเวลาดำเนินการที่ช้าที่สุด แต่จะเข้ากันได้มากที่สุดหากคุณต้องการบันทึก / เปิด / ส่ง Megapoint บนอุปกรณ์ OS X หรือ iOS ใด ๆ วิธีที่พบบ่อยที่สุดจากประสบการณ์ของฉันคือวางโครงสร้างในคลาส objc หากคุณกำลังดำเนินการทั้งหมดนี้เพื่อ (ต่อ)
justin

(ต่อ) หากคุณกำลังเผชิญกับความยุ่งยากทั้งหมดนี้เพียงเพื่อหลีกเลี่ยงการเรียนรู้ประเภทคอลเลกชันใหม่คุณควรเรียนรู้ประเภทคอลเลกชันใหม่ =) std::vector(ตัวอย่าง) เหมาะกับการถือประเภท C / C ++ โครงสร้างและคลาสมากกว่า NSArray ด้วยการใช้ NSArray ประเภท NSValue, NSData หรือ NSDictionary คุณจะสูญเสียความปลอดภัยในการพิมพ์จำนวนมากในขณะที่เพิ่มการจัดสรรและค่าใช้จ่ายรันไทม์จำนวนมาก หากคุณต้องการยึดติดกับ C โดยทั่วไปแล้วพวกเขาจะใช้ malloc และ / หรืออาร์เรย์บนสแต็ก ... แต่std::vectorซ่อนความยุ่งยากส่วนใหญ่จากคุณ
justin

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

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

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

3

จะเป็นการดีที่สุดที่จะใช้เครื่องอนุกรม objc ของคนยากจนหากคุณแชร์ข้อมูลนี้ในโครงสร้างพื้นฐาน / สถาปัตยกรรมหลายแบบ:

Megapoint mpt = /* ... */;
NSMutableDictionary * d = [NSMutableDictionary new];
assert(d);

/* optional, for your runtime/deserialization sanity-checks */
[d setValue:@"Megapoint" forKey:@"Type-Identifier"];

[d setValue:[NSNumber numberWithFloat:mpt.w] forKey:@"w"];
[d setValue:[NSNumber numberWithFloat:mpt.x] forKey:@"x"];
[d setValue:[NSNumber numberWithFloat:mpt.y] forKey:@"y"];
[d setValue:[NSNumber numberWithFloat:mpt.z] forKey:@"z"];

NSArray *a = [NSArray arrayWithObject:d];
[d release], d = 0;
/* ... */

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

หากการแสดงแบบอนุกรมไม่ออกจากกระบวนการขนาด / ลำดับ / การจัดตำแหน่งของโครงสร้างโดยพลการก็ไม่ควรเปลี่ยนแปลงและมีตัวเลือกที่ง่ายและเร็วกว่า

ในทั้งสองกรณีคุณได้เพิ่มวัตถุที่นับการอ้างอิงแล้ว (เทียบกับ NSData, NSValue) ดังนั้น ... การสร้างคลาส objc ที่มี Megapoint เป็นคำตอบที่ถูกต้องในหลาย ๆ กรณี


@ โจเป่าบางสิ่งบางอย่างที่ดำเนินการทำให้เป็นอนุกรม สำหรับการอ้างอิง: en.wikipedia.org/wiki/Serialization , parashift.com/c++-faq-lite/serialization.htmlตลอดจน "คู่มือการจัดเก็บข้อมูลและการทำให้เป็นอนุกรม" ของ Apple
justin

สมมติว่าไฟล์ xml แสดงถึงบางสิ่งอย่างถูกต้องแล้วใช่มันเป็นรูปแบบทั่วไปรูปแบบหนึ่งของการทำให้เป็นอนุกรมที่มนุษย์อ่านได้
justin

0

ฉันขอแนะนำให้คุณใช้ std :: vector หรือ std :: list สำหรับประเภท C / C ++ เพราะในตอนแรกมันเร็วกว่า NSArray และในครั้งที่สองถ้าความเร็วไม่เพียงพอสำหรับคุณคุณสามารถสร้างของคุณเองได้เสมอ ตัวจัดสรรสำหรับคอนเทนเนอร์ STL และทำให้รวดเร็วยิ่งขึ้น เอ็นจิ้นเกมฟิสิกส์และเสียงบนมือถือที่ทันสมัยทั้งหมดใช้คอนเทนเนอร์ STL เพื่อเก็บข้อมูลภายใน เพียงเพราะพวกเขาเร็วจริงๆ

ถ้าไม่ใช่สำหรับคุณ - มีคำตอบที่ดีจากผู้ชายเกี่ยวกับ NSValue - ฉันคิดว่ามันเป็นที่ยอมรับมากที่สุด


STL เป็นไลบรารีบางส่วนที่รวมอยู่ใน C ++ Standard Library en.wikipedia.org/wiki/Standard_Template_Library cplusplus.com/reference/stl/vector
Evgen Bodunov

นั่นเป็นข้อเรียกร้องที่น่าสนใจ คุณมีลิงก์ไปยังบทความเกี่ยวกับข้อได้เปรียบด้านความเร็วของคอนเทนเนอร์ STL เทียบกับคลาส Cocoa container หรือไม่?
Sedate Alien

นี่คือสิ่งที่น่าสนใจเกี่ยวกับ NSCFArray vs std :: vector: ไร้สาระ fish.com/blog/archives/2005/12/23/array ในตัวอย่างในโพสต์ของคุณการสูญเสียที่ใหญ่ที่สุดคือ (โดยทั่วไป) การสร้างการแสดงวัตถุ objc ต่อองค์ประกอบ (เช่น , NSValue, NSData หรือ Objc ประเภทที่มี Megapoint ต้องการการจัดสรรและการแทรกลงในระบบที่นับอ้างอิง) คุณสามารถหลีกเลี่ยงสิ่งนั้นได้จริงโดยใช้วิธีการของ Sedate Alien ในการจัดเก็บ Megapoint ใน CFArray พิเศษซึ่งใช้ที่เก็บสำรองแยกต่างหากของ Megapoints ที่จัดสรรอย่างต่อเนื่อง (แม้ว่าจะไม่มีตัวอย่างใดที่แสดงถึงแนวทางนั้นก็ตาม) (ต่อ)
จัสติน

แต่จากนั้นการใช้ NSCFArray เทียบกับเวกเตอร์ (หรือประเภท stl อื่น ๆ ) จะทำให้เกิดค่าใช้จ่ายเพิ่มเติมสำหรับการจัดส่งแบบไดนามิกการเรียกใช้ฟังก์ชันเพิ่มเติมที่ไม่ได้อยู่ในบรรทัดความปลอดภัยประเภทต่างๆและโอกาสมากมายที่เครื่องมือเพิ่มประสิทธิภาพจะเข้ามา ... บทความมุ่งเน้นไปที่การแทรกอ่านเดินลบ มีโอกาสที่คุณจะไม่ได้รับ c-array ที่เร็วกว่า 16 ไบต์Megapoint pt[8];- นี่คือตัวเลือกใน c ++ และคอนเทนเนอร์ c ++ พิเศษ (เช่นstd::array) - โปรดทราบว่าตัวอย่างไม่ได้เพิ่มการจัดตำแหน่งพิเศษ (เลือก 16 ไบต์ เพราะมีขนาดเท่า Megapoint) (ต่อ)
จัสติน

std::vectorจะเพิ่มค่าโสหุ้ยเล็กน้อยให้กับสิ่งนี้และการจัดสรรหนึ่งรายการ (ถ้าคุณรู้ขนาดที่คุณต้องการ) ... แต่นี่ใกล้เคียงกับโลหะมากกว่าที่เคสมากกว่า 99.9% ต้องการ โดยทั่วไปคุณจะใช้เวกเตอร์เว้นแต่ว่าขนาดจะคงที่หรือมีค่าสูงสุดที่เหมาะสม
justin

0

แทนที่จะพยายามใส่โครงสร้าง c ใน NSArray คุณสามารถใส่ไว้ใน NSData หรือ NSMutableData เป็นอาร์เรย์ ac ของโครงสร้าง ในการเข้าถึงพวกเขาคุณจะทำ

const struct MyStruct    * theStruct = (const struct MyStruct*)[myData bytes];
int                      value = theStruct[2].integerNumber;

หรือเพื่อตั้งค่าแล้ว

struct MyStruct    * theStruct = (struct MyStruct*)[myData mutableBytes];
theStruct[2].integerNumber = 10;

0

ในขณะที่การใช้ NSValue ทำงานได้ดีสำหรับการจัดเก็บโครงสร้างเป็นอ็อบเจ็กต์ Obj-C คุณไม่สามารถเข้ารหัส NSValue ที่มีโครงสร้างด้วย NSArchiver / NSKeyedArchiver แต่คุณต้องเข้ารหัสสมาชิกโครงสร้างแต่ละคน ...

ดูคู่มือการเขียนจดหมายเหตุและการทำให้เป็นอนุกรมของ Apple> โครงสร้างและฟิลด์บิต


0

สำหรับโครงสร้างของคุณคุณสามารถเพิ่มแอตทริบิวต์ objc_boxableและใช้@()ไวยากรณ์เพื่อใส่โครงสร้างของคุณลงในอินสแตนซ์ NSValue โดยไม่ต้องเรียกvalueWithBytes:objCType::

typedef struct __attribute__((objc_boxable)) _Megapoint {
    float   w,x,y,z;
} Megapoint;

NSMutableArray<NSValue*>* points = [[NSMutableArray alloc] initWithCapacity:10];
for (int i = 0; i < 10; i+= 1) {
    Megapoint mp1 = {i + 1.0, i + 2.0, i + 3.0, i + 4.0};
    [points addObject:@(mp1)];//@(mp1) creates NSValue*
}

Megapoint unarchivedPoint;
[[points lastObject] getValue:&unarchivedPoint];
//or
// [[points lastObject] getValue:&unarchivedPoint size:sizeof(Megapoint)];

-2

วัตถุ Obj C เป็นเพียงโครงสร้าง C ที่มีองค์ประกอบเพิ่มเติมบางอย่าง ดังนั้นเพียงสร้างคลาสที่กำหนดเองและคุณจะมีประเภทของโครงสร้าง C ที่ NSArray ต้องการ โครงสร้าง C ใด ๆ ที่ไม่มีส่วนเสริมที่ NSObject รวมอยู่ในโครงสร้าง C จะไม่สามารถย่อยได้กับ NSArray

การใช้ NSData เป็น Wrapper อาจเป็นการจัดเก็บสำเนาของโครงสร้างเท่านั้นไม่ใช่โครงสร้างดั้งเดิมหากสิ่งนั้นสร้างความแตกต่างให้กับคุณ


-3

คุณสามารถใช้คลาส NSObject นอกเหนือจาก C-Structures เพื่อเก็บข้อมูล และคุณสามารถจัดเก็บ NSObject นั้นไว้ใน NSArray ได้อย่างง่ายดาย

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