Khi nào nên sử dụng NSInteger so với int


344

Khi nào tôi nên sử dụng NSIntegerso với int khi phát triển cho iOS? Tôi thấy trong mã mẫu của Apple họ sử dụng NSInteger(hoặc NSUInteger) khi chuyển một giá trị làm đối số cho hàm hoặc trả về giá trị từ hàm.

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

Nhưng trong một chức năng họ chỉ sử dụng intđể theo dõi một giá trị

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

int something;
something += somethingElseThatsAnInt;
...

Tôi đã đọc (được cho biết) đó NSIntegerlà một cách an toàn để tham chiếu một số nguyên trong môi trường 64 bit hoặc 32 bit, vậy tại sao lại sử dụng int?

Câu trả lời:


322

Bạn thường muốn sử dụng NSIntegerkhi bạn không biết loại kiến ​​trúc bộ xử lý nào mà mã của bạn có thể chạy, vì vậy, vì một số lý do, bạn có thể muốn loại số nguyên lớn nhất có thể, mà trên các hệ thống 32 bit chỉ là một int, trong khi trên 64 bit hệ thống đó là a long.

Tôi sẽ sử dụng NSIntegerthay vì int/ longtrừ khi bạn đặc biệt yêu cầu chúng.

NSInteger/ NSUIntegerđược định nghĩa là * động typedef* s đối với một trong các loại này và chúng được định nghĩa như sau:

#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

Liên quan đến trình xác định định dạng chính xác mà bạn nên sử dụng cho từng loại này, hãy xem phần Hướng dẫn lập trình chuỗi trên Phụ thuộc nền tảng


4
Ngoài ra, tôi sẽ nói rằng tốt nhất là sử dụng NSInteger trừ khi bạn đặc biệt yêu cầu int hoặc long int.
v01d

4
@Shizam Có thể sử dụng một cái intsẽ phù hợp hơn với thậm chí a long. Có thể bạn biết rằng nó sẽ không vượt quá một phạm vi nhất định, và do đó nghĩ rằng nó sẽ hiệu quả hơn về bộ nhớ khi sử dụng đơn giản int.
Jacob Relkin

58
Tôi không đồng ý với câu trả lời này. Điều duy nhất tôi sẽ sử dụng NSIntegerlà chuyển các giá trị đến và từ một API chỉ định nó. Khác hơn là nó không có lợi thế hơn một int hoặc dài. Ít nhất với một int hoặc lâu bạn biết định nghĩa định dạng nào sẽ sử dụng trong một printf hoặc câu lệnh tương tự.
JeremyP

3
Điều gì xảy ra nếu bạn cần lưu trữ lâu và bạn sử dụng NSInteger trong khi bạn đang làm việc trong hệ thống 64b nhưng người dùng khác sử dụng hệ thống 32b? Bạn sẽ không nhận thấy sự thất bại nhưng người dùng sẽ.
arielcamus

14
Điều này là ngược. Luôn luôn sử dụng int trừ khi bạn có một lý do cụ thể khác. Sử dụng định nghĩa nền tảng cụ thể cho các số nguyên đơn giản không có gì ngoài việc làm cho mã của bạn khó đọc hơn.
Glenn Maynard

44

Tại sao lại sử dụng int?

Apple sử dụng intvì đối với biến điều khiển vòng lặp (chỉ được sử dụng để điều khiển vòng lặp lặp) intkiểu dữ liệu là tốt, cả về kích thước kiểu dữ liệu và trong các giá trị mà nó có thể giữ cho vòng lặp của bạn. Không cần dữ liệu phụ thuộc nền tảng ở đây. Đối với biến điều khiển vòng lặp, ngay cả 16 bit intcũng sẽ làm hầu hết thời gian.

Apple sử dụng NSIntegercho giá trị trả về của hàm hoặc cho đối số hàm vì trong trường hợp này, kiểu dữ liệu [size] có vấn đề , bởi vì những gì bạn đang làm với một hàm đang truyền / truyền dữ liệu với các chương trình khác hoặc với các đoạn mã khác; xem câu trả lời khi nào tôi nên sử dụng NSInteger vs int? trong câu hỏi của bạn ...

họ [Apple] sử dụng NSInteger (hoặc NSUInteger) khi truyền giá trị làm đối số cho hàm hoặc trả về giá trị từ hàm.


32

Hệ điều hành X là "LP64". Điều này có nghĩa rằng:

int luôn luôn là 32 bit.

long long luôn luôn là 64 bit.

NSIntegerlongluôn có kích thước con trỏ. Điều đó có nghĩa là chúng 32 bit trên hệ thống 32 bit và 64 bit trên hệ thống 64 bit.

Lý do NSInteger tồn tại là vì nhiều API di sản không đúng cách sử dụng intthay vì longđể giữ các biến con trỏ kích thước, điều này có nghĩa rằng các API đã phải thay đổi từ intđến longtrong các phiên bản 64-bit của họ. Nói cách khác, một API sẽ có các chữ ký hàm khác nhau tùy thuộc vào việc bạn biên dịch cho các kiến ​​trúc 32 bit hay 64 bit. NSIntegerdự định che giấu vấn đề này với các API kế thừa này.

Trong mã mới của bạn, sử dụng intnếu bạn cần biến 32 bit, long longnếu bạn cần số nguyên 64 bit và longhoặc NSIntegernếu bạn cần biến có kích thước con trỏ.


25
Lịch sử là tại chỗ, nhưng lời khuyên là khủng khiếp. Nếu bạn cần sử dụng biến 32 bit int32_t. Nếu bạn cần sử dụng số nguyên 64 bit int64_t. Nếu bạn cần sử dụng biến có kích thước con trỏ intptr_t.
Stephen Canon

5
Stephen, lời khuyên của bạn là đừng bao giờ sử dụng int, long hay NSInteger?
Darren

7
Không, lời khuyên của tôi là không bao giờ sử dụng chúng nếu bạn yêu cầu một loại số nguyên có kích thước cố định đã biết. Các <stdint.h>loại tồn tại cho mục đích đó.
Stephen Canon

3
Stephen, câu trả lời của tôi là để trả lời cho câu hỏi "Khi nào nên sử dụng NSInteger vs int", chứ không phải "tên kiểu chữ đa nền tảng của số nguyên 32 bit" là gì. Nếu ai đó đang cố gắng quyết định giữa NSInteger và int thì họ cũng có thể biết họ lớn như thế nào trên nền tảng mà họ hỗ trợ.
Darren

1
Cũng lưu ý rằng điều LP64đó không đảm bảo long longlà 64 bit. Một LP64nền tảng có thể chọn có long longsố nguyên 128 bit.
Stephen Canon

26

Nếu bạn đào sâu vào triển khai NSInteger:

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

Đơn giản, NSInteger typedef thực hiện một bước cho bạn: nếu kiến ​​trúc là 32 bit, nó sử dụng int, nếu là 64 bit, nó sẽ sử dụng long. Sử dụng NSInteger, bạn không cần phải lo lắng về kiến ​​trúc mà chương trình đang chạy.


14
Bạn cần phải lo lắng, bởi vì trình xác định định dạng chính xác cho NSInteger phụ thuộc vào kiến ​​trúc.
JeremyP

Cách đơn giản nhất theo hướng dẫn của Apple là chuyển giá trị sang loại số lớn nhất long long. Vì vậy, tất cả các loại số sẽ sử dụng cùng một specifier.
Eonil

6
Bây giờ cách đơn giản nhất để định dạng chỉ là đấm bốc chúng -NSLog("%@", @(1123));
Eonil

1
bạn cũng có thể sử dụng nó:NSLog("%li", (long)theNSInteger);
Daniel

casting làm tôi buồn
tomalbrc

9

Bạn nên sử dụng NSIntegers nếu bạn cần so sánh chúng với các giá trị không đổi như NSNotFound hoặc NSIntegerMax, vì các giá trị này sẽ khác nhau trên các hệ thống 32 bit và 64 bit, do đó, các giá trị chỉ số, đếm và tương tự: sử dụng NSInteger hoặc NSUInteger.

Việc sử dụng NSInteger trong hầu hết các trường hợp sẽ không gây hại gì, ngoại trừ việc nó chiếm gấp đôi bộ nhớ. Tác động bộ nhớ là rất nhỏ, nhưng nếu bạn có một số lượng lớn số lượng trôi nổi bất cứ lúc nào, nó có thể tạo ra sự khác biệt để sử dụng ints.

Nếu bạn sử dụng NSInteger hoặc NSUInteger, bạn sẽ muốn chuyển chúng thành số nguyên dài hoặc số nguyên dài không dấu khi sử dụng chuỗi định dạng, vì tính năng Xcode mới trả về cảnh báo nếu bạn thử và đăng xuất NSInteger như thể nó có độ dài đã biết. Bạn cũng nên cẩn thận khi gửi chúng đến các biến hoặc đối số được nhập dưới dạng int, vì bạn có thể mất một số độ chính xác trong quy trình.

Nhìn chung, nếu bạn không mong muốn có hàng trăm ngàn trong số chúng trong bộ nhớ cùng một lúc, thì việc sử dụng NSInteger sẽ dễ dàng hơn là liên tục lo lắng về sự khác biệt giữa hai loại.


9

Kể từ hiện tại (tháng 9 năm 2014) tôi sẽ khuyên bạn nên sử dụng NSInteger/CGFloatkhi tương tác với API của iOS, v.v. nếu bạn cũng đang xây dựng ứng dụng của mình cho arm64. Điều này là do bạn có thể sẽ nhận được kết quả bất ngờ khi bạn sử dụng float, longintcác loại.

VÍ DỤ: FLOAT / NHÂN ĐÔI vs CGFLOAT

Để làm ví dụ, chúng tôi sử dụng phương thức ủy nhiệm UITableView tableView:heightForRowAtIndexPath:.

Trong ứng dụng chỉ 32 bit, nó sẽ hoạt động tốt nếu được viết như thế này:

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

floatlà giá trị 32 bit và 44 bạn đang trả về là giá trị 32 bit. Tuy nhiên, nếu chúng ta biên dịch / chạy cùng đoạn mã này trong kiến ​​trúc arm64 64 bit thì 44 sẽ là giá trị 64 bit. Trả về giá trị 64 bit khi dự kiến ​​giá trị 32 bit sẽ cho chiều cao hàng không mong muốn.

Bạn có thể giải quyết vấn đề này bằng cách sử dụng CGFloatloại

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

Loại này đại diện cho 32 bit floattrong môi trường 32 bit và 64 bit doubletrong môi trường 64 bit. Do đó, khi sử dụng loại này, phương thức sẽ luôn nhận được loại dự kiến ​​bất kể môi trường biên dịch / thời gian chạy.

Điều này cũng đúng với các phương thức mong đợi số nguyên. Các phương thức như vậy sẽ mong đợi intgiá trị 32 bit trong môi trường 32 bit và 64 bit longtrong môi trường 64 bit. Bạn có thể giải quyết trường hợp này bằng cách sử dụng loại NSIntegerphục vụ dưới dạng inthoặc longdựa trên môi trường biên dịch / thời gian chạy.


Điều gì xảy ra nếu tôi biết giá trị của biến cụ thể này không thể chứa giá trị số lượng lớn và do đó tôi muốn sử dụng int. Nó sẽ hoạt động tốt cả trong môi trường 64 bit. Tôi nghĩ nó cũng nên như vậy, vì tôi chưa thấy một vòng lặp for như thế này: for (int i = 0; i <10; i ++) thực hiện bất kỳ hành vi sai nào bất kể môi trường được chạy trong.
Chanchal Raj

@Chanchal Raj Miễn là không có chuyển đổi hoặc chuyển đổi sang các loại khác hoặc sử dụng / ghi đè các lớp và phương thức của bên thứ ba liên quan đến biến đó, sử dụng int thay vì NSInteger sẽ ổn.
Leon Lucardie

9

Trên iOS, hiện tại không có vấn đề gì nếu bạn sử dụng inthoặc NSInteger. Nó sẽ quan trọng hơn nếu / khi iOS chuyển sang 64 bit.

Nói một cách đơn giản, NSIntegers là intmã 32 bit (và do đó dài 32 bit) và longs trên mã 64 bit ( longs trong mã 64 bit rộng 64 bit, nhưng mã 32 bit ở mã 32 bit). Lý do có khả năng nhất để sử dụng NSIntegerthay vì longlà không phá vỡ mã 32 bit hiện có (sử dụng ints).

CGFloatcó cùng một vấn đề: trên 32 bit (ít nhất là trên OS X), đó là float; trên 64-bit, nó double.

Cập nhật: Với việc giới thiệu iPhone 5s, iPad Air, iPad Mini với Retina và iOS 7, giờ đây bạn có thể xây dựng mã 64 bit trên iOS.

Cập nhật 2: Ngoài ra, sử dụng NSIntegers giúp với khả năng tương tác mã Swift.


0

int = 4 byte (kích thước không phân biệt cố định của kiến ​​trúc sư) NSInteger = phụ thuộc vào kích thước của kiến ​​trúc sư (ví dụ: đối với kiến ​​trúc sư 4 byte = kích thước NSInteger 4 byte)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.