Are you seeing errors like this:
1 2 3 4 5
Are you trying to access symbols inside an embedded framework from a static library? dylib and .a mixtures not working?
Well, I was, and here’s what I did.
iOS developers have long leveraged the static library, or
.a file. This
allowed to common code to be shared across multiple projects, or to have
multiple teams deliver into one app. It linked by static linking, which was
the only option given the tools Apple had and the sandboxing present. Most
modern programming uses shared libraries, which are dynamically linked.
- Static Library
- Embedded into the main application binary.
- Code only available to application it is linked to.
- Code loaded with application.
- Shared Library
- Shipped as a separate file along with the application binary.
- Code available to all applications it is linked to.
- Code loaded on demand.
iOS 8 introduced extensions, and with it Cocoa Touch Frameworks. These enable embedding a shared library in your application to allow code to be shared between your main application and the extensions (which are all independent binaries).
Generally, these work well and any problems you run into are likely already covered by Stack Overflow. I ran into a weird one though. Here’s the setup.
- An app that has both a static library and an embedded framework.
- The app only calls code in the static library, not the framework.
- the static library calls code provided by the framework.
If I set up the project like this, and build, it fails with the following error:
1 2 3 4 5
What’s the issue?
It turns out that since the embedded framework is a dynamic library that isn’t used directly by any app code, the symbols will only load at run time. Since the static library requires symbols to resolve at link time, this fails.
We could solve this by simply using some framework code in our app. However, this option wasn’t available to me.
This first step of the solution was to modify the library code. Instead of using the framework classes directly, we’ll use them dynamically.
Now we can build our app. However, it doesn’t actually work.
The problem is that at launch time iOS doesn’t have any reason to load our shared library. It doesn’t see any use of it (hence the original problem). So we have to force loading of the framework. We can do this in our library:
1 2 3 4 5 6 7 8 9 10 11 12 13
This works now, but the first run of a library method is slow. And what if we have multiple entry points based on user behavior, then this gets complicated.1
The final solution that I used was to move the library load inside of the app delegates
And now everything is actually working.2
So, it looks like calls to
dlcloseare actually ignored in iOS. Once you
dlopena framework, it lives as long as the app is in memory. That simplifies things some, but makes me super paranoid about not calling
We intentionally don’t worry about
dlclosesince we want symbols available throughout the entire app as long as it is running. Also, see above.↩