Review your app's data design and update it to conform with 64-bit architecture.
When you move your app to a 64-bit architecture, revisit the structures in your code. Examine C structures that have 32-bit representations. The 64-bit runtime aligns the contents of structures differently, so you need to pay particular attention to structures that store data to a file or transmit it across a network. These structures could be utilized by devices with architectures of different sizes.
Code that doesn't use explicitly sized types can cause problems for other developers who review your code. Explicitly sized type declarations clarify data-size expectations and eliminate assumptions about architectures.
Inconsistency between type declarations in a function and their use in code cause inconsistent behaviors at runtime. You need to ensure that data you pass to functions or capture as a return value are the same type as the declared parameter type.
Align 64-Bit Integer Types
As a result of the increased size of the 64-bit architecture, the alignment of all 64-bit integer types in the runtime changes from 4 bytes to 8 bytes. Even if you specify each integer type explicitly, the two structures may still not be identical in both runtimes. This is important to know if you have data that was created by the 32-bit version of your app that you need to read in the 64-bit version.
In the following code, the alignment changes even though the fields are declared with explicit integer types.
When this code is compiled with a 32-bit compiler, the field
bar begins 12 bytes from the start of the structure. When the same code is compiled with a 64-bit compiler, the field
bar begins 16 bytes from the start of the structure. Four bytes of padding are added after
foo2 so that
bar is aligned to an 8-byte boundary.
If you're defining a new data structure, organize the elements with the largest alignment values first and the smallest values last. This organization eliminates the need for most padding bytes. If you're working with an existing structure that includes misaligned 64-bit integers, you can use a pragma to force the proper alignment. The following code shows the same data structure, but here it's forced to use the 32-bit alignment rules.
Use this option sparingly because there's a performance penalty for misaligned accesses. You might use this option, for example, to maintain backward compatibility with data structures that were deployed in the 32-bit version of your app.
Use Explicit Integer Data Types
The C99 standard provides built-in data types that are guaranteed to be a specific size, regardless of the underlying hardware architecture. The following table lists the C99 types and defined ranges of values for each.
-32,768 to 32,767
-2,147,483,648 to 2,147,483,647
-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
0 to 255
0 to 65,535
0 to 4,294,967,295
0 to 18,446,744,073,709,551,615
Avoid types such as
unsigned int. Instead, use types that are guaranteed to be a specific size, regardless of the underlying hardware architecture.
Choosing a fixed-width type also avoids allocating a variable whose range is much larger than you need, and saves memory.
Keep Data Types Consistent
Inconsistent use of data types in your code can truncate results of computation and provide incorrect results. Even though the compiler warns you of many problems that result from inconsistent data types, it's also useful to see a few variations of these patterns so that you can recognize them in your code.
When calling a function, always match the variable that receives the results to the function’s return type. If the return type is a larger integer than the receiving variable, the value is truncated. The following code shows a simple pattern that exhibits this problem.
Perform function returns a long integer. In the 32-bit runtime, both
long are 32 bits, so the assignment to an
int type works, even though the code is incorrect. In the 64-bit runtime, the upper 32 bits of the result are lost when the assignment is made. Instead, the result should be assigned to a long integer; this approach works consistently in both runtimes.
The same problem occurs when you pass in a value as a parameter. Here, the input parameter is truncated when executed in the 64-bit runtime.
In the following code, the return value is also truncated in the 64-bit runtime, because the value returned exceeds the range of the function’s return type.
All of these examples result from code that assumes that
long are identical. The ANSI C standard doesn't make this assumption, and it's explicitly incorrect when working in the 64-bit runtime. By default, if you modernized your project as described in Updating Your App from 32-Bit to 64-Bit Architecture, the
-Wshorten-64-to-32 compiler option was automatically enabled, so the compiler automatically warns you about many cases where a value is truncated. If you didn't modernize your project, you should explicitly enable that compiler option. Optionally, you may want to include the
-Wconversion option, which is more verbose, but finds more potential errors.
Choose an Appropriate Data Type for Enumerated Values
In the LLVM compiler, enumerated types can define the size of the enumeration, so some enumerated types may be larger than you expect. The solution, as in all other cases, is to make no assumptions about a data type’s size. Instead, assign any enumerated values to a variable with the proper data type.
Look for Common Type-Conversion Problems
NSInteger type is used throughout the system for declaring numeric types. It's a 32-bit integer in the 32-bit runtime and a 64-bit integer in the 64-bit runtime. Never make the assumption that an
NSInteger type is the same size as an
int type. Here are a few cases in your code to look for:
Converting to or from an
Encoding and decoding data using the NSCoder class. In particular, if you encode an
NSIntegeron a 64-bit device and later decode it on a 32-bit device, the decode method throws an exception if the value exceeds the range of a 32-bit integer. You may want to use an explicit integer type instead.
Working with constants defined in the framework as
NSInteger. Of particular note is the
NSNotconstant. In the 64-bit runtime, its value is larger than the maximum range of an
inttype, so truncating its value often causes errors in your app.
Don't assume the size of CGFloat. As a result of the conversion, the
CGFloat type changes to a 64-bit floating-point number. As with the
NSInteger type, you cannot assume that
CGFloat is a
float or a
double, so use it consistently. The following code shows an example that uses Core Foundation to create a
The first part of the code assumes that a
CGFloat is the same size as a
float, which is incorrect. The second part of the code correctly specifices
k as the type to create.