Kiedy używać NSInteger vs. int

344

Kiedy powinienem używać NSIntegerkontra int przy programowaniu na iOS? Widzę w przykładowym kodzie Apple, którego używają NSInteger(lub NSUInteger), gdy przekazują wartość jako argument do funkcji lub zwracają wartość z funkcji.

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

Ale w ramach funkcji służą intdo śledzenia wartości

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

int something;
something += somethingElseThatsAnInt;
...

Przeczytałem (powiedziano), że NSIntegerjest to bezpieczny sposób na odwołanie się do liczby całkowitej w środowisku 64-bitowym lub 32-bitowym, więc po intco w ogóle korzystać?

Shizam
źródło

Odpowiedzi:

322

Zazwyczaj chcesz używać, NSIntegergdy nie wiesz, na jakiej architekturze procesora może działać Twój kod, więc z jakiegoś powodu możesz chcieć największej możliwej liczby całkowitej, która w systemach 32-bitowych jest po prostu int64-bitowa system to long.

Trzymałbym się przy użyciu NSIntegerzamiast int/, longchyba że specjalnie ich potrzebujesz.

NSInteger/ NSUIntegersą zdefiniowane jako * dynamiczne typedef* s dla jednego z tych typów i są zdefiniowane w następujący sposób:

#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

Jeśli chodzi o właściwy specyfikator formatu, którego należy użyć dla każdego z tych typów, zobacz sekcję Przewodnik programowania ciągów na temat zależności platformy

Jacob Relkin
źródło
4
Ponadto powiedziałbym, że najlepiej jest używać NSInteger, chyba że wyraźnie potrzebujesz int lub long int.
v01d
4
@Shizam Możliwe, że korzystanie z intbyłoby lepiej dostosowane nawet do long. Być może wiesz, że nie przekroczy pewnego zakresu i dlatego uważasz, że po prostu będzie bardziej wydajne pod względem pamięci int.
Jacob Relkin,
58
Nie zgadzam się z tą odpowiedzią. Jedyne, czego bym użył, NSIntegerto przekazywanie wartości do iz interfejsu API, który to określa. Poza tym nie ma przewagi nad intem ani długim. Przynajmniej int lub długi wiesz, jakich specyfikatorów formatu należy użyć w instrukcji printf lub podobnej.
JeremyP,
3
Co się stanie, jeśli będziesz musiał długo przechowywać i używasz NSInteger podczas pracy w systemie 64b, ale inny użytkownik używa systemu 32b? Nie zauważysz niepowodzenia, ale użytkownik to zauważy.
arielcamus,
14
To jest wstecz. Zawsze używaj int, chyba że masz konkretny powód. Używanie specyficznych dla platformy definicji dla prostych liczb całkowitych nie robi nic, ale sprawia, że ​​kod jest trudniejszy do odczytania.
Glenn Maynard
44

Po intco w ogóle korzystać?

Apple używa, intponieważ dla zmiennej sterującej pętli (która służy tylko do sterowania iteracjami pętli) inttyp danych jest w porządku, zarówno pod względem wielkości typu danych, jak i wartości, które może przechowywać dla twojej pętli. Nie ma potrzeby tutaj typu danych zależnego od platformy. Dla zmiennej kontrolnej pętli nawet 16-bitowejint będzie wystarczająca.

Apple używa NSIntegerwartości zwracanej przez funkcję lub argumentu funkcji, ponieważ w tym przypadku typ danych [rozmiar] ma znaczenie , ponieważ to, co robisz z funkcją, to komunikowanie / przekazywanie danych z innymi programami lub innymi fragmentami kodu; zobacz odpowiedź na pytanie Kiedy powinienem używać NSInteger vs int? w twoim pytaniu ...

[Apple] używają NSInteger (lub NSUInteger) podczas przekazywania wartości jako argumentu do funkcji lub zwracania wartości z funkcji.

Tylko ty
źródło
32

OS X to „LP64”. To znaczy że:

int jest zawsze 32-bitowy.

long long jest zawsze 64-bitowy.

NSInteger i long zawsze mają rozmiar wskaźnika. Oznacza to, że mają 32 bity w systemach 32-bitowych i 64 bity w systemach 64-bitowych.

Powodem istnienia NSInteger jest niepoprawne użycie wielu starszych interfejsów API intzamiast longprzechowywania zmiennych wielkości wskaźnika, co oznaczało, że interfejsy API musiały zmienić się z intna long64-bitowe wersje. Innymi słowy, interfejs API miałby różne sygnatury funkcji w zależności od tego, czy kompilujesz dla architektury 32-bitowej, czy 64-bitowej. NSIntegerzamierza maskować ten problem za pomocą tych starszych interfejsów API.

W nowym kodzie użyj, intjeśli potrzebujesz zmiennej 32-bitowej, long longjeśli potrzebujesz 64-bitowej liczby całkowitej, longlub NSIntegerjeśli potrzebujesz zmiennej wielkości wskaźnika.

Darren
źródło
25
Historia jest na miejscu, ale porady są okropne. Jeśli potrzebujesz zmiennej 32-bitowej, użyj int32_t. Jeśli potrzebujesz 64-bitowej liczby całkowitej int64_t. Jeśli potrzebujesz zmiennej wielkości wskaźnika, użyj intptr_t.
Stephen Canon
5
Stephen, twoja rada to nigdy nie używać int, long ani NSInteger?
Darren
7
Nie, radzę, aby nigdy ich nie używać, jeśli potrzebujesz liczby całkowitej o znanym stałym rozmiarze. Te <stdint.h>typy istnieją do tego celu.
Stephen Canon
3
Stephen, moja odpowiedź była odpowiedzią na pytanie „Kiedy używać NSInteger vs int”, a nie „jaka jest wieloplatformowa nazwa 32-bitowa liczba całkowita”. Jeśli ktoś próbuje zdecydować między NSInteger a int, równie dobrze może wiedzieć, jak duży jest na obsługiwanych platformach.
Darren,
1
Zauważ też, że LP64nie gwarantuje long longto 64 bitów. LP64Platforma mogła zdecydować się long longbyć 128 bitową liczbą całkowitą.
Stephen Canon
26

Jeśli zagłębisz się w implementację NSInteger:

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

Po prostu typedef NSInteger robi krok za Tobą: jeśli architektura jest 32-bitowa, używa int, jeśli jest 64-bitowa, używa long. Korzystając z NSInteger, nie musisz się martwić architekturą, na której działa program.

Evan Mulawski
źródło
14
Musisz się martwić, ponieważ właściwy specyfikator formatu dla NSInteger zależy od architektury.
JeremyP,
Według instrukcji Apple najprostszym sposobem jest rzutowanie wartości na największy typ liczbowy long long. Dlatego wszystkie typy liczbowe będą używać tego samego specyfikatora typów.
Eonil
6
Teraz najprostszym sposobem na sformatowanie jest po prostu boksowanie -NSLog("%@", @(1123));
Eonil
1
możesz też rzucić:NSLog("%li", (long)theNSInteger);
Daniel
casting mnie zasmuca
tomalbrc
9

Powinieneś użyć NSIntegers, jeśli chcesz porównać je ze stałymi wartościami, takimi jak NSNotFound lub NSIntegerMax, ponieważ wartości te będą się różnić w systemach 32-bitowych i 64-bitowych, więc wartości indeksu, liczby i tym podobne: użyj NSInteger lub NSUInteger.

Używanie NSInteger w większości przypadków nie zaszkodzi, z wyjątkiem tego, że zajmuje dwa razy więcej pamięci. Wpływ na pamięć jest bardzo niewielki, ale jeśli w danym momencie unosi się ogromna liczba liczb, użycie ints może mieć znaczenie.

Jeśli używasz NSInteger lub NSUInteger, będziesz chciał rzutować je na długie liczby całkowite lub niepodpisane długie liczby całkowite podczas korzystania z ciągów formatujących, ponieważ nowa funkcja Xcode zwraca ostrzeżenie, jeśli spróbujesz wylogować się z NSInteger, jakby miał znaną długość. Podobnie powinieneś zachować ostrożność, wysyłając je do zmiennych lub argumentów wpisanych jako ints, ponieważ możesz stracić trochę precyzji w procesie.

Ogólnie rzecz biorąc, jeśli nie spodziewasz się, że w pamięci będą znajdować się setki tysięcy z nich, łatwiej jest używać NSInteger, niż stale martwić się różnicą między nimi.

Popiół
źródło
9

Na dzień dzisiejszy (wrzesień 2014 r.) Zalecam używanie NSInteger/CGFloatpodczas interakcji z interfejsami API iOS itp., Jeśli budujesz również swoją aplikację dla arm64. Wynika to z faktu, że prawdopodobnie uzyskasz nieoczekiwane wyniki podczas korzystania z float, longiint typy.

PRZYKŁAD: FLOAT / DOUBLE vs CGFLOAT

Jako przykład bierzemy metodę delegowania UITableView tableView:heightForRowAtIndexPath:.

W aplikacji tylko 32-bitowej będzie działać poprawnie, jeśli zostanie napisany w następujący sposób:

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

floatjest wartością 32-bitową, a zwracana wartość 44 jest wartością 32-bitową. Jeśli jednak skompilujemy / uruchomimy ten sam fragment kodu w 64-bitowej architekturze arm64, wartość 44 będzie wartością 64-bitową. Zwrócenie wartości 64-bitowej, gdy oczekiwana jest wartość 32-bitowa, da nieoczekiwaną wysokość wiersza.

Możesz rozwiązać ten problem, używając CGFloattypu

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

Ten typ reprezentuje 32-bit floatw środowisku 32-bitowym i 64-bit doublew środowisku 64-bitowym. Dlatego podczas korzystania z tego typu metoda zawsze otrzyma oczekiwany typ niezależnie od środowiska kompilacji / środowiska wykonawczego.

To samo dotyczy metod, które oczekują liczb całkowitych. Takie metody będą oczekiwać intwartości 32-bitowej w środowisku 32-bitowym i 64-bitowej longw środowisku 64-bitowym. Można rozwiązać ten przypadek za pomocą typ NSInteger, który służy jako intlub longna podstawie kompilacji / Runtime environemnt.

Leon Lucardie
źródło
Co jeśli wiem, że wartość tej konkretnej zmiennej nie może zawierać dużych liczb i dlatego chcę użyć int. Czy będzie działać dobrze zarówno w środowisku 64-bitowym. Myślę, że tak też powinno być, ponieważ nie widziałem takiej pętli for: for (int i = 0; i <10; i ++) robi jakiekolwiek złe zachowanie niezależnie od środowiska, w którym jest uruchamiany.
Chanchal Raj
@Chanchal Raj Tak długo, jak nie będzie rzutowania ani konwersji na inne typy ani użycia / przesłonięcia klas i metod innych firm wykorzystujących tę zmienną, użycie int zamiast NSInteger będzie w porządku.
Leon Lucardie,
9

W systemie iOS obecnie nie ma znaczenia, czy używasz intlub NSInteger. Będzie to miało większe znaczenie, jeśli / kiedy iOS przejdzie na 64-bit.

Mówiąc najprościej, NSIntegers są intw kodzie 32-bitowym (a zatem 32-bitowym) i longs są w kodzie 64-bitowym ( longs w kodzie 64-bitowym mają szerokość 64-bitową, ale 32-bit w kodzie 32-bitowym). Najbardziej prawdopodobnym powodem użycia NSIntegerzamiast longnie jest łamanie istniejącego 32-bitowego kodu (który używa ints).

CGFloatma ten sam problem: w wersji 32-bitowej (przynajmniej w OS X) jest float; w wersji 64-bitowej double.

Aktualizacja: Wraz z wprowadzeniem iPhone'a 5s, iPada Air, iPada Mini z Retiną i iOS 7 możesz teraz budować 64-bitowy kod na iOS.

Aktualizacja 2: Ponadto użycie NSIntegers pomaga we współdziałaniu kodu Swift.

MaddTheSane
źródło
0

int = 4 bajty (ustalony niezależnie od wielkości architekta) NSInteger = zależy od wielkości architekta (np. dla 4 bajtów architekta = 4 bajty wielkości NSInteger)

Anargit garg
źródło