piątek, 19 czerwca 2015

Clang compiler under the hood


When you build an iOS application you're not really interested with how does it all work under the hood most of the times. You just place some layout, hit "Run" or "Archive" and that's it. It just works.

It's when you start building your own library you start wondering: how does it all work?

Static Library

Clang is no different than GCC when it comes to compiling and linking. When you compile a static library you create a .a file. The so called archive file is nothing more than just a package of compiled object files. The .o files, which are in ELF format contain not only code, but also the roadmap of the file: symbols and data. This data is needed for the linker to resolve symbols at executable linking time.

When you include a static library to your executable, symbols and code are copied into the file. With -dead_strip flag enabled only the symbols used in your executable are used - it helps in reducing the executable file size.


example of "nm" and "file" programs
You can use nm to list all symbols in a file. As you can see, there are no symbols for functions, because of of Objective-C`s dynamic nature. Function symbols are internal (-g flag of nm shows only external symbols). Because of this, if you have any categories in your static library, you have to put a -ObjC or some other flag like -force_load to your linker flags, or you'll get a crash at runtime, because the functions of the category will not be copied at all, as they are not referenced anywhere. Unfortunately it makes the executable file larger*.

Symbols with U are "unresolved" meaning that they are to be resolved during the executable link time (in case of static libraries).

During the final executable link time, the linker creates a table and searches for all the needed symbols in defined search paths. If it doesn't find a needed symbol it stops the linking process with something you might be familiar with:


oops

Duplicate Symbols Problem

Objective-C doesn't have namespaces. That's why you see every library with a two or three letter prefix. It helps avoids collisions. If someone creates a class XXXModel and links his program with a static library that uses YYYModel he doesn't get a collision.

What if your static library uses AFNetworking and your library's users want to use AFNetworking too ? They'll get a collision. The most common way of solving that is to add a prefix to AFNetworking.

The other way is: using dynamic libraries.

Dynamic Libraries

A dynamic library unlike the static library isn't just a collection of code to be linked. It's a collection of code ready to be executed.

When you link your executable the dynamic library has to be present so the linker know what to reference, but the code doesn't get copied to your executable.
source


When a system loads an executable, and that executable has references to dynamic libraries, the dynamic linker searches for these libraries, loads them to RAM fills the code jump pointers for the executable.

Dynamic libraries can drastically reduce the size of an executable, not only because the code doesn't get copied, but also because you get to dynamically load AFNetworking once, not get it precompiled in every library you use.

iOS8 enabled using shared libraries, but they still get copied into the bundle. The only difference is that you can share a library between all your iOS extensions.

CocoaPods

One of CocoaPods' jobs is to manage dependencies. If you include 100 dependencies that depend on AFNetworking it'll only include the code once, not one hundred times. But that's the case for open-source software, not pre-compiled libraries.

-ObjC flag*

As I mentioned earlier -ObjC makes your executable larger, because it forces to load all files containing Objective-C code.

As I mentioned earlier categories are not loaded, because they are not referenced anywhere. What if we were to force reference them ?

Just declare any symbol in the file:


declare an extern int variable

declare the variable
Now you have to use that symbol somewhere so it gets link referenced:

That's it! Now you don't have to force your static library users to link with -ObjC flag.