ควรใช้ NSInteger vs. int เมื่อใด


344

เมื่อใดที่ฉันควรใช้NSIntegervs. int เมื่อพัฒนาสำหรับ iOS ฉันเห็นในโค้ดตัวอย่างของ Apple ที่ใช้NSInteger(หรือNSUInteger ) เมื่อส่งค่าเป็นอาร์กิวเมนต์ไปยังฟังก์ชันหรือส่งคืนค่าจากฟังก์ชัน

- (NSInteger)someFunc;...
- (void)someFuncWithInt:(NSInteger)value;...

แต่ภายในฟังก์ชั่นพวกเขาใช้intเพื่อติดตามค่า

for (int i; i < something; i++)
...

int something;
something += somethingElseThatsAnInt;
...

ฉันอ่านแล้ว (ได้รับการบอก) ว่าNSIntegerเป็นวิธีที่ปลอดภัยในการอ้างอิงจำนวนเต็มในสภาพแวดล้อม 64- บิตหรือ 32- บิตดังนั้นทำไมจึงใช้งานintได้ทั้งหมด

คำตอบ:


322

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

ฉันจะใช้NSIntegerแทนการใช้int/ longยกเว้นว่าคุณต้องการมันเป็นพิเศษ

NSInteger/ NSUIntegerถูกกำหนดเป็น * dynamic typedef* s ให้กับหนึ่งในประเภทเหล่านี้และถูกกำหนดเช่นนี้:

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

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


4
นอกจากนี้ฉันจะบอกว่ามันเป็นการดีที่สุดที่จะใช้ NSInteger เว้นแต่ว่าคุณต้องการ int หรือ long int เป็นพิเศษ
v01d

4
@Shizam มันเป็นไปได้ว่าการใช้จะดีกว่าที่จะเหมาะแม้กระทั่งint longบางทีคุณอาจรู้ว่ามันไม่เกินช่วงที่กำหนดและดังนั้นจึงคิดว่ามันจะใช้หน่วยความจำได้อย่างมีประสิทธิภาพintมากขึ้น
Jacob Relkin

58
ฉันไม่เห็นด้วยกับคำตอบนี้ สิ่งเดียวที่ฉันจะใช้NSIntegerคือส่งค่าไปยังและจาก API ที่ระบุ นอกเหนือจากนั้นมันไม่มีความได้เปรียบเหนือ int หรือเป็นเวลานาน อย่างน้อยด้วย int หรือ long คุณจะรู้ว่าตัวระบุรูปแบบใดที่จะใช้ใน printf หรือคำสั่งที่คล้ายกัน
JeremyP

3
จะเกิดอะไรขึ้นถ้าคุณต้องการเก็บข้อมูลไว้นานและคุณใช้ NSInteger ในขณะที่คุณกำลังทำงานในระบบ 64b แต่ผู้ใช้รายอื่นใช้ระบบ 32b คุณจะไม่สังเกตเห็นความล้มเหลว แต่ผู้ใช้จะ
arielcamus

14
นี่คือถอยหลัง ใช้ int ทุกครั้งยกเว้นว่าคุณมีเหตุผลเฉพาะ การใช้การระบุเฉพาะแพลตฟอร์มสำหรับจำนวนเต็มอย่างง่ายไม่ได้ทำอะไรเลย แต่ทำให้โค้ดของคุณอ่านยากขึ้น
Glenn Maynard

44

ทำไมต้องใช้งานintเลย?

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

Apple ใช้NSIntegerสำหรับค่าส่งคืนฟังก์ชั่นหรืออาร์กิวเมนต์ของฟังก์ชันเพราะในกรณีนี้ประเภทข้อมูล [ขนาด] มีความสำคัญเพราะสิ่งที่คุณทำกับฟังก์ชั่นคือการสื่อสาร / ส่งผ่านข้อมูลกับโปรแกรมอื่นหรือรหัสอื่น ๆ ดูคำตอบของฉันควรใช้ NSInteger vs int เมื่อใด ในคำถามของคุณเอง ...

พวกเขา [Apple] ใช้ NSInteger (หรือ NSUInteger) เมื่อส่งค่าเป็นอาร์กิวเมนต์ให้กับฟังก์ชันหรือส่งคืนค่าจากฟังก์ชัน


32

OS X คือ "LP64" ซึ่งหมายความว่า:

int อยู่เสมอ 32- บิต

long long เป็น 64- บิตเสมอ

NSIntegerและlongมีขนาดตัวชี้เสมอ นั่นหมายความว่าพวกมันเป็นแบบ 32 บิตบนระบบ 32 บิตและ 64 บิตบนระบบ 64 บิต

เหตุผลที่ NSInteger นั้นมีอยู่เพราะ API ดั้งเดิมจำนวนมากใช้อย่างไม่ถูกต้องintแทนที่จะlongถือตัวแปรขนาดตัวชี้ซึ่งหมายความว่า API ต้องเปลี่ยนจากintเป็นlongเวอร์ชัน 64 บิต กล่าวอีกนัยหนึ่ง API จะมีลายเซ็นของฟังก์ชั่นที่แตกต่างกันขึ้นอยู่กับว่าคุณกำลังรวบรวมสถาปัตยกรรมแบบ 32 บิตหรือ 64 บิต NSIntegerตั้งใจที่จะปิดบังปัญหานี้ด้วย API ดั้งเดิมเหล่านี้

ในรหัสใหม่ของคุณใช้intถ้าคุณต้องการตัวแปร 32 บิตlong longหากคุณต้องการจำนวนเต็ม 64 บิตและlongหรือNSIntegerถ้าคุณต้องการตัวแปรขนาดตัวชี้


25
ประวัติศาสตร์เป็นจุดสำคัญ แต่คำแนะนำนั้นแย่มาก int32_tหากคุณต้องการใช้ตัวแปร 32 บิต int64_tหากคุณต้องการใช้จำนวนเต็ม 64 บิต intptr_tหากคุณต้องการใช้ตัวแปรชี้ขนาด
Stephen Canon

5
สตีเฟ่นคำแนะนำของคุณคืออย่าใช้ int, long หรือ NSInteger ในตอนนั้น?
Darren

7
ไม่คำแนะนำของฉันคืออย่าใช้มันหากคุณต้องการขนาดคงที่ที่ทราบจำนวนเต็ม <stdint.h>ประเภทที่มีอยู่สำหรับวัตถุประสงค์ที่
Stephen Canon

3
สตีเฟ่นคำตอบของฉันคือตอบคำถามที่ว่า "เมื่อใดจึงควรใช้ NSInteger vs int" ไม่ใช่ "ชื่อพิมพ์ข้ามแพลตฟอร์มของจำนวนเต็ม 32 บิตคืออะไร" หากใครบางคนพยายามที่จะตัดสินใจระหว่าง NSInteger และ int พวกเขาอาจรู้ว่าพวกเขาใหญ่แค่ไหนบนแพลตฟอร์มที่พวกเขาสนับสนุน
Darren

1
โปรดทราบว่าLP64ไม่รับประกันว่าlong longจะเป็น 64 บิต LP64แพลตฟอร์มสามารถเลือกที่จะมีlong longเป็นจำนวนเต็ม 128 บิต
Stephen Canon

26

หากคุณขุดเข้าไปในการนำไปใช้ของ NSInteger:

#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
#endif

เพียงแค่การ typedef NSInteger ไม่เป็นขั้นตอนสำหรับคุณ: ถ้าสถาปัตยกรรม 32 บิตจะใช้intถ้ามันเป็นแบบ 64 longบิตจะใช้ การใช้ NSInteger คุณไม่จำเป็นต้องกังวลเกี่ยวกับสถาปัตยกรรมที่โปรแกรมทำงานอยู่


14
คุณจำเป็นต้องกังวลเนื่องจากตัวระบุรูปแบบที่ถูกต้องสำหรับ NSInteger ขึ้นอยู่กับสถาปัตยกรรม
JeremyP

long longวิธีที่ง่ายตามคู่มือแอปเปิ้ลหล่อมูลค่าให้กับประเภทที่เป็นตัวเลขที่ใหญ่ที่สุด ดังนั้นประเภทตัวเลขทั้งหมดจะใช้ตัวระบุชนิดเดียวกัน
Eonil

6
ตอนนี้วิธีที่ง่ายที่สุดในการจัดรูปแบบเป็นเพียงมวยพวกเขา -NSLog("%@", @(1123));
Eonil

1
คุณสามารถโยนมันได้:NSLog("%li", (long)theNSInteger);
แดเนียล

การหล่อทำให้ฉันเศร้า
tomalbrc

9

คุณควรใช้ NSIntegers หากคุณต้องการเปรียบเทียบกับค่าคงที่เช่น NSNotFound หรือ NSIntegerMax เนื่องจากค่าเหล่านี้จะแตกต่างกันไปในระบบ 32- บิตและ 64- บิตดังนั้นค่าดัชนีจำนวนและสิ่งที่คล้ายกัน: ใช้ NSInteger หรือ NSUInteger

ไม่เจ็บที่จะใช้ NSInteger ในสถานการณ์ส่วนใหญ่ยกเว้นว่าใช้หน่วยความจำมากถึงสองเท่า ผลกระทบของหน่วยความจำมีขนาดเล็กมาก แต่ถ้าคุณมีตัวเลขจำนวนมากลอยอยู่ในแต่ละครั้งมันอาจสร้างความแตกต่างในการใช้ ints

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

โดยรวมแล้วหากคุณไม่คาดหวังว่าจะมีหลายแสนคนในหน่วยความจำในครั้งเดียวมันง่ายกว่าที่จะใช้ NSInteger มากกว่ากังวลเกี่ยวกับความแตกต่างระหว่างสองอย่างต่อเนื่อง


9

ณ ปัจจุบัน (กันยายน 2014) ฉันขอแนะนำให้ใช้NSInteger/CGFloatเมื่อต้องโต้ตอบกับ iOS API และอื่น ๆ หากคุณกำลังสร้างแอปของคุณสำหรับ arm64 นี้เป็นเพราะคุณมีแนวโน้มที่จะได้รับผลที่ไม่คาดคิดเมื่อคุณใช้float, longและintประเภท

ตัวอย่าง: FLOAT / DOUBLE เทียบกับ CGFLOAT

ตัวอย่างเช่นเราใช้วิธีการมอบหมาย UITableView tableView:heightForRowAtIndexPath:ตัวอย่างเช่นเราใช้วิธีการที่ผู้รับมอบสิทธิ์

ในแอปพลิเคชัน 32- บิตเท่านั้นมันจะทำงานได้ดีถ้ามันเขียนดังนี้:

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

floatคือค่า 32- บิตและ 44 ที่คุณส่งคืนคือค่า 32- บิต อย่างไรก็ตามหากเราคอมไพล์ / รันโค้ดเดียวกันนี้ในสถาปัตยกรรม 64 บิต 64 บิต 44 จะเป็นค่า 64 บิต การส่งคืนค่า 64 บิตเมื่อคาดหวังว่าค่า 32 บิตจะให้ความสูงของแถวที่ไม่คาดคิด

คุณสามารถแก้ไขปัญหานี้ได้โดยใช้CGFloatประเภท

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

ชนิดนี้แสดงถึง 32- บิตfloatในสภาพแวดล้อม 32- บิตและ 64- บิตdoubleในสภาพแวดล้อม 64- บิต ดังนั้นเมื่อใช้ประเภทนี้วิธีการจะได้รับประเภทที่คาดหวังเสมอโดยไม่คำนึงถึงสภาพแวดล้อมการคอมไพล์ / รันไทม์

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


จะเกิดอะไรขึ้นถ้าฉันรู้ว่าค่าของตัวแปรนี้ไม่สามารถมีค่าจำนวนมากและฉันต้องการใช้ int ดังนั้น มันจะทำงานได้ดีทั้งในสภาพแวดล้อม 64 บิต ฉันคิดว่ามันควรจะเหมือนกันเพราะฉันไม่ได้เห็นการวนซ้ำเช่นนี้: สำหรับ (int i = 0; i <10; i ++) ทำพฤติกรรมผิด ๆ โดยไม่คำนึงถึงสภาพแวดล้อมที่มันทำงานอยู่
Chanchal Raj

@Chanchal Raj ตราบใดที่ไม่มีการแคสต์หรือแปลงเป็นประเภทอื่นหรือการใช้งาน / การแทนที่ของคลาสบุคคลที่สามและวิธีการที่เกี่ยวข้องกับตัวแปรนั้นการใช้ int แทน NSInteger จะถูกปรับ
Leon Lucardie

9

บน iOS ปัจจุบันมันไม่สำคัญว่าถ้าคุณใช้หรือint NSIntegerมันจะมีความสำคัญมากกว่านี้หาก / เมื่อ iOS เลื่อนไปเป็น 64- บิต

เพียงแค่ใส่NSIntegers เป็นintรหัส 32 บิต (และยาว 32 บิต) และlongs ในรหัส 64 บิต ( longs ในรหัส 64 บิตกว้าง 64 บิต แต่ 32 บิตในรหัส 32 บิต) สาเหตุที่เป็นไปได้มากที่สุดสำหรับการใช้งานNSIntegerแทนlongคือไม่แบ่งรหัส 32 บิตที่มีอยู่ (ซึ่งใช้int)

CGFloatมีปัญหาเดียวกัน: ใน 32- บิต (อย่างน้อยใน OS X) มันfloat; เมื่อวันที่ 64 doubleบิตมัน

อัปเดต:ด้วยการเปิดตัว iPhone 5s, iPad Air, iPad Mini พร้อม Retina และ iOS 7 ตอนนี้คุณสามารถสร้างรหัส 64 บิตบน iOS ได้แล้ว

อัปเดต 2:นอกจากนี้การใช้NSIntegers ช่วยด้วยการทำงานร่วมกันของรหัส Swift


0

int = 4 ไบต์ (ขนาดที่ไม่ จำกัด คงที่ของสถาปนิก) NSInteger = ขึ้นอยู่กับขนาดของสถาปนิก (เช่นสำหรับสถาปนิก 4 ไบต์ = 4 ไบต์ขนาด NSInteger)

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