`
lovebirdegg
  • 浏览: 174968 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

Universal Static Libraries

阅读更多

For some reason Apple reserves the ability to create iPhone frameworks for their own use. They are a very useful ability to have because they package headers together with a universal binary. „Universal“ meaning that you get binary code for multiple platforms rolled into a single file. Frameworks are also dynamically loaded which means that less binary code fills up your memory as only stuff is put there that is actually needed.

But alas, no frameworks for us.

Until now I am selling my components in the Dr. Touch’s Parts Store as source code which at the same time means that I could not provide demonstration versions to try out in your own code. If you have the code, what incentive is there to pay the price?

It has been suggested to me by several people independently to make a static library to solve this problem. Could be time- or otherwise limited, but then people could try the parts out and see how well they fit with their own work.

Also next to still offering access to my repositories at full price, I could be selling indidual versions without right to upgrade to get started at a substantial discount. Later if you find that the component really IS worth what I am asking for, you could upgrade to the lifetime upgrade plan via the SVN repo.

Another reason to figure out this Static Library Magic is so that I can bundle my own utility classes that I keep adding to and improving in a central place. This gives me the choice of adding an external SVN reference or to simply copy the static library into my new projects.

In this article I am describing how to create a universal static library for both Intel and Arm architectures, how to glue them together into a single file and how to add it to a new demo project.

Step 1 – Code for a new utility static library

Start out by creating an iPhone static library project.

So that there is actually code inside our static library we create a class extension for NSURL to give us a dictionary of all the parameters passed in a URL. This is quite useful when analyzing the parameters from the applicationion:didFinishLaunchingWithOptions if your app is launched via its URL scheme.

NSURL+DT.h

@interface

 NSURL



 (

DT)


 
// dictiary which contains param = value for each parameter


-

 (

NSDictionary



 *

)

 dictionaryOfParameters;
 
@end

NSURL+DT.m

#import "NSURL+DT.h"


 
@implementation

 NSURL



 (

DT)


 
-

 (

NSDictionary



 *

)

 dictionaryOfParameters
{


	NSString



 *

paramName;
	NSString



 *

paramValue;
 
	NSMutableDictionary



 *

tmpDict =

 [

NSMutableDictionary



 dictionary]

;
 
	NSScanner



 *

scanner =

 [

NSScanner



 scannerWithString:

[

self query]

]

;
 
	// NOTE: This cannot deal with values that contain & or similar HTML entities


	while

 (

!

[

scanner isAtEnd]

)


	{


		[

scanner scanUpToString:

@

"="

 intoString:&

paramName]

;
		[

scanner scanString:

@

"="

 intoString:

nil

]

;
		[

scanner scanUpToString:

@

"&"

 intoString:&

paramValue]

;
		[

scanner scanString:

@

"&"

 intoString:

nil

]

;
 
		[

tmpDict setObject:

paramValue forKey:

paramName]

;
	}


 
	return

 [

NSDictionary



 dictionaryWithDictionary:

tmpDict]

;
}


 
@end

Drag the header into the „Copy Headers“ section of the target. Drag the implementation file into the „Compile Sources“ section. Contrary to a regular iPhone app target you have to do that manually so that your static library actually contains anything to compile. Also note that the build steps differ from those for an app target.

Step 2 – Set up the targets

Your default Cocoa Touch static library comes with a single target. We want to be able to build for multiple targets with different architectures. So first we change the current target’s base SDK to iPhone SDK 3.0. This will build it for the arm6 and arm7 platforms. Rename the target by appending Dev to the target name so that we know this is the one set up for device.

Next we duplicate the target by right-clicking on it and „Duplicate“. Rename the second target by appending Sim to the name to signal that this is the one built for simulator.

We also set the base SDK for the Sim target to be the same version but for simulator. This causes the build to be made for the i386 platform.

The SDK you choose in the upper lefthand box overrides the set base SDK for your targets. So if you want the selected SDKs to be built for you have to set it to „Use Base SDK“. If this is set then building the individual targets obeys the individually set base SDK.

Let’s try building both targets. There is no Build-All option yet, so we build both targets one after the other.

It’s no problem that they have the same output file name because due to the different SDK they end up in different subfolders of your build folder, Debug-iphonesimulator and Debug-iphoneos. If you like you can get them renamed by changing the product name of the targets, but that’s optional. The following shows the layout of your project after all setup but before the builds as the products are still red.

Step 3 – A „Build All“ Target with merging

We don’t want to have to build all targets individually every time and also we still need to glue them together. So we create a new aggregation target which we call „Build & Merge Libraries“

To this combation target we add our previous targets as dependencies. This will cause the building of sub-targets if they are outdated.

Apple provides the lipo tool for merging multiple binaries into one. Most likely you have it already installed at /usr/bin/lipo, mine is dated July 3rd 2009 and I did not manually install it.

We add a new “Run Script Build Phase” after the two sub-targets and fill in our code to make a new output folder and merge the libraries.

Here’s the code. It basically instructs lipo where to find the two libraries for the different architectures and where to write the merged result lib archive.

# make a new output folder
mkdir -p ${PROJECT_DIR}/build/${BUILD_STYLE}-iphoneos/DTUtilities
 
# combine lib files for various platforms into one
lipo -create "${PROJECT_DIR}/build/${BUILD_STYLE}-iphoneos/libDTUtilities.a" "${PROJECT_DIR}/build/${BUILD_STYLE}-iphonesimulator/libDTUtilities.a" -output "${PROJECT_DIR}/build/${BUILD_STYLE}-iphoneos/DTUtilities/libDTUtilities-${BUILD_STYLE}.a"

Step 4 – Package it for distribution

If you want to use the library in another project or hand to somebody else you have to add the headers and any other resources (like images) that it uses. A framework would have taken care of this for us, but we need to transfer these files manually. So we’ll make two extra build steps to automate the copying of files and zipping it into a handy archive.

We add a new “Copy Files Build Phase” and set it up so that our header is copied. We want the header to go into our subfolder of the products folder.

Drag the header into this phase. If there where any PNGs or other resources we would drag them into here as well.

Finally, so that we can send it off we want to make a compressed archive out of our result. This can be accomplished by yet another run script build phase. I found that the ditto command does the best job of making archives that look like you made them in Finder.

ditto -c -k --keepParent "${PROJECT_DIR}/build/${BUILD_STYLE}-iphoneos/DTUtilities" "${PROJECT_DIR}/build/${BUILD_STYLE}-iphoneos/DTUtilities.zip"

So in total our Build & Merge target looks like this:

If we now build this target, provided that we have chosen Base SDK in the top left picker, we get a ZIP file that contains all our resources and the merged static library with code that can be linked with arm6, arm7 and Intel binaries.

Step 5 – Use it in a different project

From this point on, this diverges from the topic of this article. Therefore if all you where looking for was how to make a Universal Static Library you can stop reading right now.

To actually use/test this library we’ll make a very simple project that just logs the parameters passed if our demo app is called via an URL scheme.

We create a new project for a new View-based application. Then we drag the header and library from our distribution folder into our project and choose that we want it to be copied to our project. Note that Xcode automatically adds the library to the linker phase of the target.

So that we can test the calling via URL we’ll add some code to the app delegate to NSLog the passed parameters. Don’t forget the import.

-

 (

BOOL

)

application:

(

UIApplication *

)

application didFinishLaunchingWithOptions:

(

NSDictionary



 *

)

launchOptions {


	NSLog(

@

"%@"

, launchOptions)

;
	NSURL



 *

launchURL =

 [

launchOptions objectForKey:

UIApplicationLaunchOptionsURLKey]

;
 
	NSDictionary



 *

parameters =

 [

launchURL dictionaryOfParameters]

;
	NSLog(

@

"%@"

, parameters)

;
 
    // Override point for customization after app launch


    [

window addSubview:

viewController.view]

;
    [

window makeKeyAndVisible]

;
 
	return

 YES

;
}

We need to add a URL scheme to our app so that it can be launched via URL. This is done by adding a URL identifier and a URL scheme to the info plist.

Now if we where to simply try this out we would get an exception that our dictionaryOfParameters method is an unrecognized selector. The reason being that C and C++ generates symbols for each function whereas Objective-C only generates one per class. We need to force the linker (ld ) to also load the members of the class. This is achieved by the linker flag -ObjC.

The second problem being that NSURL itself is in a different archive than our category extension. It’s in fact in a dynamically bound object file in one of Apple’s frameworks. Thus the linker thinks it’s already got all that makes up NSURL and does not load our category extension. At runtime it turns out that this assumption was false and thus we get the crash. Fortunately we can force inclusion of all our objects from our static library by adding the linker flag -all_load.

-all_load will include all members of all classes from all your static libraries. If  you happen to have dozens of static libraries and you want to force the full loading only of individual ones you can use the -force-load parameter as an alternative because it allows you to specify a single library archive to completely load. But for a single static library it’s probably ok to go with all_load.

These flags need to be added to the target’s “Other Linker Flags”.

Note: If you built previously you have to clean the build and make a new build for linker flag changes to take effect.

We can now try out our app. We hit Build&Run once so that we get the latest version installed. Next we go into mobile Safari in iPhone Simulator and enter a URL that begins with demo:// so that the iPhone OS starts our app.

This causes Safari to quit and our app to start. The results from NSLog in simulator also end up in the console app where we can see that all is working. The first NSLog shows the options dictionary, the second the dictionary that our category extension method returned.

Ok, Simulator works, that alone does not fully convince us. We’ll also want to try it out on device to see if the linker was able to get the arm code from the merged library. We’ll use a nifty feature of the debugger to have it not start the app, but instead wait until the next launch.

For the LibDemo executable set the debugger settings to “Wait for next launch/push notification”.

If we now debug then Xcode will not start our app, but wait faithfully until it is launched the next time. So if we repeat our test in Mobile Safari, this time on device, we’ll see on the console the same result proving that it works.

<script type="text/javascript">&lt;!-- google_ad_client = &quot;pub-1142183725909145&quot;; google_ad_slot = &quot;4110438702&quot;; google_ad_width = 468; google_ad_height = 60; //--&gt;</script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script><script>google_protectAndRun(&quot;ads_core.google_render_ad&quot;, google_handleError, google_render_ad);</script>

Conclusion

You can see that it is really not very difficult to package your code into static libraries that combine multiple architectures, in our case i386, arm6 and arm7. This way you can let other people try out our components or classes without having to hand them the source code. They only need the headers, the lib archive (.a) file and any resources (like PNGs) that your components are using.

We’ve been using a Debug build of our library all this time. This preserves the debug symbols for our own development. But for release and distribution builds we will probably want to make a Release build of the library so that debug symbols are stripped out.

Now that I have learned these steps I will start making static libraries for all my components on the Dr. Touch’s Parts Store so that people interested in using them have a way to try before they buy. I’m yet uncertain how I want to limit the functionality though. Maybe I’ll superimpose a big translucent “SAMPLE” over a control or maybe I’ll add some time limiting functionality. I’ll figure it out.

Besides of being able to provide free samples I am also contemplating offering one-time-only purchases of all my components without free upgrades to major new versions and without source code. This could be offered at a fraction of the cost of the source code itself.

So universal static library are a powerful tool to have in your arsenal as iPhone developer. Still be hope that some day Apple lets us make frameworks for iPhone as well.

分享到:
评论

相关推荐

    FFmpeg 4.2.2 static libraries for macOS.rar

    在这个“FFmpeg 4.2.2 static libraries for macOS.rar”压缩包中,包含了FFmpeg在macOS平台上的静态库文件,非常适合开发者在构建自己的多媒体应用时使用,如播放器、视频剪辑工具或视频下载工具。 1. **FFmpeg ...

    在动态库(dynamic libraries)和静态库(static libraries)使用模板(template)

    在C++编程中,动态库(Dynamic Libraries)和静态库(Static Libraries)是两种常见的代码复用方式。模板(Templates)则是一种强大的泛型编程工具,允许我们在编译时创建类型安全的通用代码。理解如何在动态库和...

    在动态库和静态库中使用模板(dynamic libraries ,static libraries)

    libraries可以分为两类:静态库(static libraries)和动态库(dynamic libraries)。静态库是一种将所有库成员都编译到目标文件中的库,而动态库是一种可以在运行时被加载和卸载的库。然而,在使用动态库和静态库...

    Agilent IO Libraries Suite 14.2

    Agilent IO Libraries Suite 14.2,支持配合安捷伦IntuiLink使用,是安捷伦仪器连接电脑必备的软件。不同版本IO Libraries并不能兼容,所以不是最新的就是最好。

    Altium Designer 10 Libraries

    Altium Designer 10 Libraries是面向电子设计自动化(EDA)领域的专业资源库,它为电路板设计师提供了丰富的元器件模型和符号,以便于在Altium Designer 10这个集成化的PCB设计软件中进行设计工作。这个资源库包含了...

    Protel DOS Schematic Libraries.ddb

    protel99se 元件库:Protel DOS Schematic Libraries.ddb

    Agilent IO Libraries Suite 15.5.rar

    1. **仪器控制**:安捷伦IO Libraries Suite的核心功能是能够控制各种安捷伦品牌的仪器设备,如示波器、逻辑分析仪、频谱分析仪等,通过GPIB(通用接口总线)、USB、以太网等多种接口实现远程操作。 2. **驱动程序...

    Agilent_IO_Libraries_suite_14.2.rar

    Agilent IO Libraries Suite 14.2,支持配合安捷伦IntuiLink使用,是安捷伦仪器连接电脑必备的软件。不同版本IO Libraries并不能兼容,所以不是最新的就是最好。

    MySQ LoadRunner libraries下载

    MySQL LoadRunner Libraries是针对MySQL数据库在使用HP LoadRunner进行性能测试时所需的一组特定库文件。LoadRunner是一款功能强大的性能测试工具,它允许用户模拟大量虚拟用户来测试系统在高负载下的性能,以确保...

    Agilent_IO_Libraries_Suite15.5(winxp).rar

    VISA提供了一组API(应用程序编程接口),涵盖了串行、并行、GPIB(通用接口总线)、USB、以太网等多种通信接口,使得开发者可以轻松地编写控制程序,而无需深入理解底层硬件通信协议。 在Agilent IO Libraries ...

    DSP2833x_Libraries 开发库函数

    **DSP2833x_Libraries 开发库函数详解** TI的DSP2833x系列是高性能浮点数字信号处理器(DSP),广泛应用于通信、工业控制、音频处理和许多其他领域。这些器件以其强大的计算能力、低功耗以及丰富的外设接口而受到...

    Axure Libraries.zip

    "Axure Libraries.zip" 是一个包含预设元件库的压缩包,专为Axure RP9用户设计,帮助他们提高设计效率,减少重复工作。这个压缩包中的“Axure Libraries”很可能包含了各种预定义的组件、图标、按钮、表单元素和其他...

    Altium Designer元件库Libraries.zip

    Download Libraries Old Content - visit altium.com/documentation Modified by Admin on Sep 13, 2017 Download all Libraries: Download all Libraries, in single ZIP file (305MB) ...

    golang+onnxruntime-win-x64-static-lib-1.16.3加载yolov8的模型

    This repo provides pre-compiled onnxruntime shared and static libraries for various platforms. 此存储库为各种平台提供预编译的 onnxruntime 共享库和静态库。 可以在golang里面结合github....

    Program Library Howto

    This HOWTO for programmers discusses how to create and use program libraries on Linux. This includes static libraries, shared libraries, and dynamically loaded libraries.

    Axure_RP_Libraries分享.zip

    这个名为"Axure_RP_Libraries分享.zip"的压缩包文件包含了一系列Axure RP的库资源,这些库通常由预设的组件、动态面板、样式和交互等组成,目的是为了提高设计效率,让用户能够快速地复用已有的设计元素。...

    POCO C++ Libraries Introduction

    The POCO C++ Libraries are... &gt; a collection of C++ class libraries, similar in concept to the Java Class Library, the .NET Framework or Apple's Cocoa; &gt; focused on "internet-age" network-centric ...

    CUDA-Libraries_cuda_libraries_

    CUDA库是NVIDIA公司开发的一系列用于加速计算的软件库,它们使得开发者能够利用CUDA编程模型在GPU(图形处理单元)上执行并行计算任务,从而显著提升计算效率。CUDA库为高性能计算、科学模拟、图像处理、机器学习等...

    Axure92个常用组件Libraries.rar

    这个名为"Axure92个常用组件Libraries.rar"的压缩包包含了92个适用于Axure 9的常见组件库,旨在帮助设计师快速搭建产品原型,提高设计效率。这些组件涵盖了网页、移动应用、表单以及各种交互元素,使得原型设计过程...

Global site tag (gtag.js) - Google Analytics