查看文章 |
Apple iPhone Developer agreement forbids using Frameworks in iPhone applications. The main reasons Apple does not allow using them is performance considerations and that developer-private frameworks won’t be reused in memory between iPhone applications anyway. But this doesn’t prohibit 3rd party iPhone developers to create static libraries for reusable code and possibly even distribute them for other developers while not providing source-code. In this blog post I’ll try to describe the process of:
Nearly everything in the following applies to Mac OS X static libraries and projects too. I’ll indicated the differences. Create a static libraryAlthough Xcode doesn’t have iPhone OS Static Library template as it do for Mac OS X, we can use Static Library → Cocoa Static Library template and switch SDK for it or we can create iPhone application project based on iPhone OS → Window-Based Application template and then create new iPhone OS Static Library Target what Xcode has for iPhone projects. I’ve chosen to use iPhone OS template because created for us iPhone app Target will come in handy anyway, for example as a demo project or for some real-world testing of static library. So, let’s create a project what a bit later will be the home of our new static library. I’ll name it “LibDemo”. Select File → New Project… → iPhone OS → Window-Based Application.
Next, let’s create a Static Library Target. Right click on Targets,
I’ll name the Target “libDemo” (with lower “l”). Now we have two Targets:
and two Products:
Right click on libDemo Target, select “Get Info”, click on “Build” tab. Search for “Product Name” and be shure to use “All Configurations” configuration. Set Product name to “Demo”, Xcode adds standard static library
Fine, now we have following Products:
Next we can create some dummy class what will be compiled in our static library and what we will use in dependent project. Create a class for libDemoI’ll create Select File → New File… → Cocoa Touch Classes → NSObject subclass This class should be compiled in libDemo so select both LibDemo and libDemo Targets.
Static library headersFine, now we have a class with a header and in dependent project (depending on setup) we have the choice of using all static library headers or just subset of them what we declare as “public” here (like when creating Frameworks). I’ve chosen to use only public headers so I can be sure I’m not using anything I consider private from static library. So, if I’m distributing static library in binary form with public headers only I can be confident I haven’t anything forgotten to add to them. To declare header public, we need to set it’s Role to “public”. Select Targets → libDemo and set “public” as Role for AMZeeba.h:
..and build our library. Built static libraryXcode creates the following structure in librarie’s build folder: ampatspell:~/Cocoa/static/LibDemo/build/Debug-iphonesimulator$ tree And while we’re here, let’s look at library symbol table: ampatspell:~/Cocoa/static/LibDemo/build/Debug-iphonesimulator$ nm libDemo.a As we can see, the public headers are copied to Everything looks fine, we can continue with libDemo Client ApplicationIn general there are (at least) two ways how to include code from static library project in dependent application. The simplest is just dragging headers and implementation files to dependent project and adding them to desired Target. Other way is actually to link against static library and it’s preferred way from code organisation point of view. I’ll start with linking and then, in next section, briefly describe “dragging way” with it’s benefits and drawbacks. Before we start setting-up linking against our static library, we need to create “Client” iPhone project. Select File → New Project… → iPhone OS → Application → Window-Based Application I’ll name this project “Client”. Build & Run. We should see blank, white window background in iPhone Simulator or on actual device. Linking against static libraryTo link against a static library while continuing to work with both dependent application and static library we need to:
Quite a list of tasks to create this setup but fortunately it’s easy to do. We will start with first. Cross-Project ReferenceWe need to start with a cross-project reference to static library project. This is Xcode specific feature what allows us to use Products and set build dependencies between projects. Drag blue LibDemo project icon from “Groups & Files” and drop it inside Client project. Check “Client” in “Add To Targets” table, do not check “Copy items into destination group’s folder” in the sheet what will appear:
Press Add, the result should look like this:
As you can see, there’s LibDemo.xcodeproj reference what lists two Products — libDemo.a and LibDemo.app. We’re not interested in LibDemo.app what eventually will be just a demo of libDemo.a functionality (or just disappear). But libDemo.a is the product we will use by linking against it. Link binary, Direct dependencyTo link binary with a static library we need to add it to “Linked Libraries” list for binary Target. Unfold LibDemo.xcodeproj and Targets → Client → Link Binary With Libraries. Drag libDemo.a to Link build phase.
Now we’re linking Client binary against our staric library but it doesn’t mean the library is automatically rebuilt if we make changes in it’s code. To inform Xcode we want to keep library up-to-date and link against newest version in this Target, we need to set library as direct dependency for Target. To set libDemo library Target as dependency for Client Target, right click on Client Target, select Get Info then click on General tab. Click add button under Direct Dependencies list, select libDemo.
As you can see, this panel consists of two parts:
Actually we could use Add button under Linked Libraries to link against To make sure our direct dependency setting works, let’s clean both projects then build only Client. This should trigger building of Select Build → Clean (⇧⌘K) and click “Also Clean Dependencies” Select Build → Build (⌘B) and Build → Build Results (⇧⌘B) We should see that two Targets are built successfully:
But before we can start actually using library, we need to tell Xcode where to find library headers. Public HeadersFirst I want to show the problem. Let’s add
Of course, Client’s app project has no idea where to look for External headers in Xcode are searched using “Header Search Path” (and “User Header Search Path”) build variables. We need to add Open “Client” Target info panel (Select Targets → Client and press ⌘I), type “header search” in search field under “Build” tab.
Double-click on “Header Search Path” and add: ${PROJECT_DIR}/../libDemo/build/${BUILD_STYLE}-${PLATFORM_NAME}/usr/local/include
The variables are:
Note: For Mac OS X Static libraries the search path value is: ${PROJECT_DIR}/../libDemo/build/${BUILD_STYLE}/usr/local/include
Grand finaleNow we can build Client and it should find
…and this is the result we where looking for. Download LibDemo & Client (36Kb) Dragging thingCreating a static library is not the only way how to create reusable code “bundles” for iPhone and for Cocoa development in general. On Mac OS X the preferred way is creating Frameworks but this is not an option for iPhone but there’s third way — dragging and dropping group of files from one project into another and adding them to desired Targets. This way we’re cross-project referencing source code. This has some benefits and drawbacks. The benefit is simpler setup — we don’t need to create a static library Target, no header search pathes to set. The drawbacks to name a few are:
In conclusion I must mention that it’s possible to mix static library approach with code cross-project references. It may be beneficial to start with static library by using drag-and-drop referencing and when library grows or there’s need to distribute static library in binary form, switch to static linking. |
















