For the More Curious: Build Phases, Compiler Errors, and Linker Errors

Building an application in Xcode takes several steps. We call these steps build phases, and you saw them earlier in this chapter when you were adding the Core Location framework to the Whereami target (Figure 4.1). Here is what each build phase does:

Compile Sources
This build phase contains the source code files that are compiled when this target is built. By default, any time you add a source code file to a project, it is added to this build phase.
Link Binary With Libraries
After your source code has been compiled, it is linked with the frameworks (libraries). This allows your code to use classes from these frameworks.
Copy Bundle Resources
After your code is compiled and linked, an executable is created and placed inside an application bundle, which is really just a folder. Then, the files listed in the Copy Bundle Resources phase are added to the bundle alongside the executable. These resources are the data files that your application uses at runtime, like MainWindow.xib and any images or sounds that are part of the application. By default, when you add a file to a project that is not source code, it is added to this build phase.

We usually see errors during the Compile Sources phase, but sometimes we get errors during the Link Binary With Libraries phase. Errors generated during these phases are easier to diagnose and correct if you understand what the phases do.

Preprocessing

The Compile Sources build phase can be broken into two steps: preprocessing and compiling. The goal of the preprocessing phase is to create an intermediate file for each implementation file (.m). The intermediate file is still Objective-C code like the implementation file, but, as we will see, the intermediate file can get very large.

To create an intermediate file, the preprocessor resolves all the preprocessor directives in the implementation file. Preprocessor directives are statements prefixed with the pound symbol (#), like #import. The resolution of a #import statement replaces the import statement with the contents of the imported file. (You can view the contents of imported files by Command-clicking the import statement.)

For example, consider WhereamiAppDelegate.m, which imports WhereamiAppDelegate.h. The intermediate file created for WhereamiAppDelegate.m contains all the code from WhereamiAppDelegate.h and WhereamiAppDelegate.m. But, it doesn’t stop there. WhereamiAppDelegate.h imports two files, UIKit.h and CoreLocation.h. These two files import more header files, which import more header files, and so on. The intermediate file for WhereamiAppDelegate.m is all of the code in all of these files (Figure 4.10).

Figure 4.10  Preprocessor creates intermediate files

Preprocessor creates intermediate files

Compiling

Once the preprocessor has finished, the generated intermediate files are compiled. Compiling an intermediate file takes the Objective-C code and turns it into machine code. This machine code is stored in an object file, one for each intermediate file.

The compiling phase – the transition to machine code – is where we see most of our errors as programmers. When the compiler doesn’t understand our code, it generates an error. We call errors generated during this phase compile-time errors or syntax errors. Compile-time errors are typically misplaced semicolons, unbalanced brackets ([]) or braces ({}), spelling or capitalization errors.

These types of errors also occur when you use a class that hasn’t been declared. To see an example of a compile-time error, comment out the following line in WhereamiAppDelegate.h:

/​/​ ​#​i​m​p​o​r​t​ ​<​C​o​r​e​L​o​c​a​t​i​o​n​/​C​o​r​e​L​o​c​a​t​i​o​n​.​h​>​

Build the application again, and the compile phase will fail. To see the problem up close, click the Compiling icon to open the issue navigator or hit Command-4. This navigator shows you any errors or warnings in your code (Figure 4.11). You can click on an individual error to see the line of code that generated the error.

Figure 4.11  Build results with compile-time error

Build results with compile-time error

Before you removed the import statement, the intermediate file created from WhereamiAppDelegate.m contained the code from CoreLocation.h, which contained the interface declaration for CLLocationManager and the protocol declaration for CLLocationManagerDelegate. Without the import statement, these files do not become part of the generated intermediate file, and the compiler has no idea what to do with these lines of code. Note that the compiler can only read one intermediate file at a time, so even though the class and protocol are in other intermediate files, the compiler still generates an error when they are not declared for WhereamiAppDelegate.m.

Uncomment the import statement. You should be able to build again with no errors.

Linking

An object file contains the machine code for the methods implemented in the implementation file. However, within an implementation file, you use code from other implementation files. For example, WhereamiAppDelegate.m uses the startUpdatingLocation method, and the machine code for that method is in the object file generated from CLLocationManager.m.

Instead of copying the code for this method into the object file for WhereamiAppDelegate.m, the compiler leaves a link to the object file for CLLocationManager.m. The Link Binary With Libraries phase is where these links are resolved. For short, we just call it the linking phase.

Recall earlier in the chapter that you linked the Core Location framework to your target. A framework is a collection of classes, and a class is defined by two files: a header file and an implementation file. A framework, however, has pre-compiled its implementation files and shoved the resulting object files into one or more library files. (That’s why in Objective-C you can’t see the implementation files in a framework – they are already machine code.) Where you used code from the classes in the Core Location framework in your classes, the compiler put a link in your object files to the Core Location library (Figure 4.12).

Figure 4.12  Compiler creates object files; linker resolves links

Compiler creates object files; linker resolves links

If a link cannot be resolved (because the object file that contains the code cannot be found or because the object file doesn’t contain the referenced code), you get a linker error. Linker errors are more difficult for new developers to understand because they use unfamiliar terms and because there isn’t one line of code that generated the error. So let’s cause a linker error just for practice. Select CoreLocation.framework from the project navigator and hit the Delete key. On the window that appears, choose Remove Reference Only. Build the application again, and you will see new errors (Figure 4.13).

Figure 4.13  Build results with linker error

Build results with linker error

You can select an error in the issue navigator to see a more detailed description of what is going on. (You can also go to the log navigator and select the most recent Build Whereami item and see the same information.) Notice that the errors generated are underneath the item named Link. When you see linker errors, it is typically because you did not add the appropriate framework to your target. Add the CoreLocation.framework file back to the target in the Link Binary With Libraries build phase and build again to confirm that you have fixed the error.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.235.188