.iOS静态库相关-封装lib

Library介绍

基本知识

在实际的编程过程中,通常会把一些公用函数制成函数库,供其它程序使用,一则提搞了代码的复用;二则提搞了核心技术的保密程度。

Library使用的两种方式:封装lib.a和直接引用lib工程。

一、封装.a文件

直接封装lib.a,向使用者提供头文件列表。使用者引用头文件并且使用其中方法,但是看不到实现文件的内容。这种方式每当静态库函数需要修改时就必须重新生成lib.a提供给使用者更换,比较麻烦,但有助于保密。

制作静态库

New Project -> iOS Library ->Cocoa touch Static Library 这样就新建了一个静态库的工程,将你要打包成lib.m,.h放到class目录下面,然后选择build就可以了

Bulid之后,在工程目录下Produces文件夹下可以看到生成的.a文件引用,右键,show in finder可以看到.a文件。

要注意Build时的选项:

<1>iOS Device编译出来的是在Debug-iphoneos目录下,真机使用,终端,在该目录下使用lipo -info **.a 可以查看你到文件类型为armv7ARM架构。

<2>Simulator时编译出来的是在Debug-iphonesimulator目录下,模拟器使用,终端查看类型显示为i386架构。

可以使用lipo命令生成一个通用二进制lib.a lipo -create **/**.a **/**.a -output **/**.a 生成一个兼容两种类型的.a文件。方法虽好,但是包大小会增加。

<3>.a文件所在木有没有include文件夹,如何设置?

在项目Target设置页面选择Build Phases,然后选中里面的某一项(必须选择一下,否则后面的操作不能进行),然后菜单栏Editor->add Build Phases->Add Copy Build Phases即可生成Copy Files,在里面配置生成的路径及需要生成的头文件,选择Product Directory,路径例如:include/$(PRODUCT_NAME),然后Clean-Build即可发现.a文件所在目录多了一个include目录,包含了配置好的头文件。

使用静态库

在需要调用静态库的工程的目录下通过右键点 Frameworks->Add->Existing Files..添加之前创建的.a静态库文件,然后在需要调用静态库的函数的文件里,import进来静态库中.h头文件,这样就可以使用静态库里的函数了。(此处可以做一个头文件包含静态库中所有的头文件,只需声明这一个头文件就可以使用所有的相应头文件的方法)

问题及注意事项

0. .a文件路径:/Users/user/Library/Developer/Xcode/DerivedData/****/Build/Products/

不同模式下可以生成不同类型的.a文件 真机/模拟器与Debug/Release选项公交叉成4.a文件。     

1.打包分清楚是debugRelease的。

选择debugReleaseXcode工具栏的Product选项现则Scheme->Edit Scheme.然后为各个运行模式选择选项。    

2.分清楚libi386(真机)或者ArmV7(模拟器)模式

终端下使用命令 lipo -info libPrint.a 可以查看.a的属性。如结果:libPrint.a is architecture(构建): armv7 

3.把真机运行和模拟器运行的.a文件合并生成通用的.a文件,完成通用的静态库。

终端使用命令 lipo -create 真机.a路径 模拟器.a路径 -output 目标路径(/users/user/desktop/***.a)。然后info查看合并后.a的信息就会发现它已经同时具备了armv7i386的条件

4.Build Phases->Compile Source中的文件,表示这些代码会被编译进lib中,你可以删掉你不希望被编译的。

5.标准的Unix引入惯例是一个include文件夹,用来存放所有引用的外部头文件,一个lib文件夹用来存放库文件(.a)。这种文件夹结构这是一种惯例,并不强制。

:自动生成通用lib.a

生成通用二进制lib.a需要lipo,一个命令行工具,它允许在通用文件上执行操作(类似于创建通用二进制, 列出通用文件内容等等)。本教程中使用lipo的目的是联合不同架构的二进制文件到单个输出文件中。你可以直接在命令行中使用lipo命令,但在本教程中你可以让Xcode执行一段创建通用库的命令行脚本来为你做这件事。

Xcode中一个集合目标可以一次构建多个目标,包括命令行脚本。在选中lib工程文件,点击+号增加新的Target,选择iOS/Other并点击Aggregate,如下图:

20140514231051093.png

将目标命名为UniversalLib,确保选中你的lib工程中。然后选择UniversalLib Target。切换到Build Phases标签;点击+号增加Add Run Script Build Phase,如下图:20140514231109484.png

现在你需要设置脚本项。展开Run Script模块,在Shell行下粘贴如下代码:

 

# define output folder environment variable

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

TARGET_NAME=ProjectName

# Step 1. Build Device and Simulator versions

xcodebuild -target ${TARGET_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

xcodebuild -target ${TARGET_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

 

# make sure the output directory exists

mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

 

# Step 2. Create universal binary file using lipo

lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"

 

# Last touch. copy the header files. Just for convenience

cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/"

注意:修改其中的TARGET_NAME=ProjectName为你的lib工程名。

 

代码并不十分复杂,它是这样工作的:

UNIVERSAL_OUTPUTFOLDER 包括了通用二进制包将要被存放的文件夹:“Debug-universal”

Step 1. 2行执行了xcodebuild并命令它构建ARM架构的二进制文件。

下一行再次执行了xcodebuild命令并在另一个文件夹中构建了一个针对Inter架构的iPhone模拟器的二进制文件,在这里关键参数是-sdk iphonesimulator -arch i386

Step 2. 现在已经有了2.a文件分别对应两个架构。执行lipo -create,用它们创建出一个通用二进制。

最后一行的作用是复制头文件到通用构建文件夹的外层。

现在你已经准备好构建一个静态库的通用版本。

选择UniversalLib然后Run,你就会在产品目录发现一个新的文件夹Debug-Universal或者Release-Universal,里面包含了合并之后的lib.a以及头文件。

详细操作参考链接:http://www.cocoachina.com/applenews/devnews/2013/1204/7468.html

二、引用lib工程

静态库工程被包含在项目工程中或者与项目工程放在同一个WorkSpace中,做成联调静态库。这种方式的静态库工程与项目工程一起使用,故没有对Libray中的代码进行封装,可以查看修改。

创建联调工程   

1.在工程的Targets上右键.Add -> New Target -> Static Library 比如我们建了一个LibExampletarget。这样是一个工程包含多个Target的形式,没有新建Lib工程。创建好Target之后你会发现原来的工程下面会多出几个文件夹:LibExampleLibExampleTest,用来存放跟Library相关的代码。

另外,也可以直接在原工程上右键新建一个lib工程,或者在工程中右键add Existing File..增加已经存在的lib工程进来(不要选择copy to folder)。这种形式是一个工程下面包含一个Lib工程。

2.LibExample的目录中增加你需要加入的.h.m文件,然后查看在Build Phases->Compile Source中的文件,表示这些代码会被编译进lib中,你可以删掉你不希望被编译的,增加你想要编译进去的文件。

3在工程的target上双击,targets->Build Phases里面Target Dependencies里面增加lib工程的target,这样编译工程时也会编译lib工程生成lib.a文件。同时在Link Binary With Libraries中增加选择lib.a,表示对library库的引用。 

4.使用Lib工程而非Target时,需要修改工程的Scheme->Build中增增加Lib工程的Target。这样才能编译工程的同时编译lib工程,生成.a

5.引用lib头文件:在项目文件工程文件的targetbuild Setting->Header Search Paths中增加头文件路径(../文件名(lib工程文件名/ 例如../MyLibPrint/),这个路径适应于lib工程与项目工程在同一目录),选择成递归类型。

6.最后在工程中可以使用lib.a中的文件了,使用时引用一下lib工程的头文件,如果不报错说明头文件引用成功,然后就可以使用了。

Lib相关部分错误信息

1.undefine symbols for architecture i386 错误。

其实这个错误原因很简单,就是因为,我们用错了编译出来的libUITab.a lib

在模拟器里面,我们需要的是基于i386构架编译的static lib,但是这个a文件,大家还记得前面说的arm6 arm7构架的么。这个a其实是在iphone这个arm构架上运行的代码。

那如何编译i386的库呢?运行之前选择Print>IOS Device,将这个iOS Device修改成iPhone5.0 Simulator。在进行编译,这样就可以编译出i386下面的库。

下面最多有四个文件夹分别命名为:Debug-iphoneos/Debug-iphonesimulator/Release-iphoneos/Release-iphonesimulator这四条目录每个目录下同样也有一个libPrint.a文件。Release-iphoneos里面的是基于arm6 arm7编译出来的库文件。Release-iphonesimulator文件夹下面的是基于i386编译出来的文件。

2.在编译RegexKitLite的时候,报错如下:

在项目的编译设置中找到Other Linker Flags,然后在后面字段空白处双击,添加“-licucore”就可以了,引用正则框架必须打开此开关。-licucore,注意不要打错,打错了会报错误:clang: error: no such file or directory: ‘licucore'

64bit的支持

Xcode5.1生成的项目,默认的便已选择是支持64bit编译的,可以查看工程的build Setting中的Architectures选项,包含了arm64的设置。

项目支持arm64要求项目中所引用的lib.a.framework文件均支持arm64,否则就会报错。

ld: symbol(s) not found for architecture x86_64

当然,可以直接设置项目不支持x86_64

  • build Settings中在Architectures选项设置为armv7armv7s
  • Valid Architectures中删除arm64

但是能兼容肯定是更好的:

  • lib或者framework工程创建是需要包含对arm64的支持。查看上述选项如果没有需要添加。
  • 使用lipo -info查看是否包含x86-64,判断是否成功。

使用本文中的通用工程生成时,在模拟器编译时需要修改为如下,

xcodebuild -target ${TARGET_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 -arch x86_64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

增加ONLY_ACTIVE_ARCH=NO-arch x86_64。然后编译即可生成符合要求的.a

其他参考

Library官方文档:https://developer.apple.com/library/ios/technotes/iOSStaticLibraries/Introduction.html#//apple_ref/doc/uid/TP40012554-CH1-SW1

XCode的各种参数配置参考:http://www.cnblogs.com/xiaodao/archive/2012/03/28/2422091.html



标签: xcode制作静态库


展开阅读全文

如何封装静态

08-27

#include rn#include rn#include rn#include rn#include rn#define ITEMS_NUM 10 //缓冲区的数组元素个数rnvoid Render();rnvoid GameLoop();rnHRESULT InitD3D(HWND hWnd);rnLPDIRECT3D9 g_pD3D =NULL;rnLPDIRECT3DDEVICE9 g_pD3DDevice =NULL;rnLPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer =NULL;rnLPDIRECTINPUT Input=NULL;rnLPDIRECTINPUTDEVICE InputDevice= NULL;rnchar keyBuffer[256];rnvoid CleanUp();rnstruct CUETOMVERTEXrnrn FLOAT x,y,z,rhw;rn DWORD color;rn;rn#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)rnHINSTANCE hInst;rnHWND hWnd;rnDIDEVICEOBJECTDATA DeviceObjectData[ITEMS_NUM];//缓冲区数组rnrnlong MouseMoveX = 0;//鼠标X轴的总移动量rnlong MouseMoveY = 0;//鼠标Y轴的总移动量rnrnPOINT cur_point;//鼠标当前位置rnCHAR tmpText[50];rnHDC hdc;rnrnHRESULT InitD3DInput();rnHRESULT InitVertexBuffer();rnLRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);rn#define SafeRelease(pObject) if(pObject!=NULL)pObject->Release();pObject =NULL;rnint WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)rnrn hInst = hInstance;rn WNDCLASSEX wcex;rn wcex.cbSize = sizeof(WNDCLASSEX);rnrn wcex.style = CS_CLASSDC;rn wcex.lpfnWndProc = WndProc;rn wcex.cbClsExtra = 0;rn wcex.cbWndExtra = 0;rn wcex.hInstance = hInstance;rn wcex.hIcon = NULL;rn wcex.hCursor = NULL;rn wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);rn wcex.lpszMenuName = NULL;rn wcex.lpszClassName = "base";rn wcex.hIconSm = NULL;rnrn RegisterClassEx(&wcex);rn hWnd = CreateWindow("base", "DirectX", WS_OVERLAPPEDWINDOW,rn 50, 50, 500, 500, NULL, NULL, wcex.hInstance, NULL);rn if(SUCCEEDED(InitD3D(hWnd)))rn rn ShowWindow(hWnd, nCmdShow);rn UpdateWindow(hWnd);rn if(SUCCEEDED(InitD3DInput()))rn rn SetWindowPos(hWnd,0,0,0,0,0,SWP_NOSIZE);//设置应用程序窗口位置rn SetCursorPos(0,0);//设置鼠标位置rn hdc = GetDC(hWnd);rn GameLoop();rn rn rn CleanUp();rn UnregisterClass("base",wcex.hInstance);rnrn return 0;rnrnrnLRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)rnrnrn switch(message)rn rn case WM_DESTROY:rn PostQuitMessage(0);rn return 0;rn case WM_KEYUP:rn switch(wParam)rn rn case VK_ESCAPE:rn DestroyWindow(hWnd);rn break;rn rn return 0;rn rn return DefWindowProc(hWnd, message, wParam, lParam);rnrnvoid GameLoop()rnrn MSG msg;rn bool fMessage;rn PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);rn while(msg.message !=WM_QUIT)rn rn fMessage = PeekMessage(&msg,NULL,0,0,PM_REMOVE);rn if(fMessage)rn rn TranslateMessage(&msg);rn DispatchMessage(&msg);rn rn elsern rn Render();rn rn rnrnvoid Render()rnrn if( g_pD3DDevice ) // Only use Device methods if we have a valid device.rn rn HRESULT hr;rn hr=InputDevice->GetDeviceState(sizeof(keyBuffer),(LPVOID)keyBuffer);rn if(hr==DIERR_INPUTLOST)rn rn InputDevice->Acquire(); //重新获取键盘的使用权rn hr=InputDevice->GetDeviceState(sizeof(keyBuffer),(LPVOID)keyBuffer);rn if(FAILED(hr))rn rn ::MessageBox(0, " DIERR_NOTACQUIRED ", 0, 0);rn return ;rn rn rn if(keyBuffer[DIK_RIGHT] & 0x80)rn MessageBox(NULL,"RIGHT_KEY_PRESSED","提示",0);rnrn if(keyBuffer[DIK_LEFT] & 0x80)rn MessageBox(NULL ,"LEFT_KEY_PRESSED ","提示",0);rnrn if((keyBuffer[DIK_A] & 0x80)&(keyBuffer[DIK_LCONTROL] & 0x80)) rn MessageBox(NULL ,"Ctrl+A_PRESSED ","提示",0);rnrn // Instruct the device to set each pixel on the back buffer black -rn // D3DCLEAR_TARGET: 0x00000000 (black) - and to set each pixel onrn // the depth buffer to a value of 1.0 - D3DCLEAR_ZBUFFER: 1.0f.rn g_pD3DDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);rnrn // Swap the back and front buffers.rn g_pD3DDevice->Present(0, 0, 0, 0);rn rn return ;rnrnHRESULT InitD3D(HWND hWnd)rnrn g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);rn if(g_pD3D==NULL)rn rn return E_FAIL;rn rn D3DDISPLAYMODE d3ddm;rn if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT ,&d3ddm)))rn rn return E_FAIL;rn rn D3DPRESENT_PARAMETERS d3dpp;rn ZeroMemory(&d3dpp,sizeof(d3dpp));rn d3dpp.Windowed = TRUE;rn d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;rn d3dpp.BackBufferFormat = d3ddm.Format;rnrn if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,rn D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,rn &g_pD3DDevice)))rn rn return E_FAIL;rn rn return S_OK;rnrnvoid CleanUp()rnrn SafeRelease(g_pVertexBuffer);rn SafeRelease(g_pD3DDevice);rn SafeRelease(g_pD3D);rnrnHRESULT InitD3DInput()rnrn HRESULT hr=0;rn hr = DirectInput8Create( rn hInst, rn DIRECTINPUT_VERSION,rn IID_IDirectInput8,rn (void**)&Input, rn NULL);rn if(FAILED(hr))rn rn ::MessageBox(0, "DirectInput8Create( ) - FAILED", 0, 0);rn return false;rn rnrn //创建键盘输入设备rn hr=Input->CreateDevice(GUID_SysKeyboard, &InputDevice, NULL);rn if(FAILED(hr))rn rn ::MessageBox(0, "CreateDevice ( )-KEYBOD-FAILED", 0, 0);rn return false;rn rnrn //设置键盘输入设备的数据格式rn hr=InputDevice->SetDataFormat(&c_dfDIKeyboard);rn if(FAILED(hr))rn rn ::MessageBox(0, " SetDataFormat( )-FAILED", 0, 0);rn return false;rn rnrn //设置键盘输入设备的协调级别rn hr=InputDevice->SetCooperativeLevel(rn hWnd,rn DISCL_BACKGROUND|DISCL_NONEXCLUSIVErn );rn if(FAILED(hr))rn rn ::MessageBox(0, " SetCooperativeLevel( )-FAILED", 0, 0);rn return false;rn rnrn //获取键盘输入设备的访问权rn hr=InputDevice->Acquire();rn if(FAILED(hr))rn rn ::MessageBox(0, " Acquire( )-FAILED", 0, 0);rn return false;rn rnrn //键盘缓冲区清零rn ZeroMemory(keyBuffer,sizeof(char)*256);rn return true;rnrn以上是代码!如何封装键盘响应的静态库!菜鸟求解 论坛

没有更多推荐了,返回首页