美文网首页
ios 创建动(静)态库

ios 创建动(静)态库

作者: 不老歌_Bloger | 来源:发表于2019-08-13 16:36 被阅读0次

为什么要使用动(静)态库?

**静态库的好处**:

        1、模块化,分工合作

        2、避免少量改动经常导致大量的重复编译连接

        3、也可以重用,注意不是共享使用

**动态库使用有如下好处**:

        1、使用动态库,可以将最终可执行文件体积缩小

        2、使用动态库,多个应用程序共享内存中得同一份库文件,节省资源

        3、使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。

**结论: **将整个应用程序分模块,团队合作,进行分工,影响比较小等其他好处,

苹果iOS开发中其实是禁止使用动态库的,即使可以使用也是不纯正的动态库,是无法使用共享功能的.在 iOS 8 之前,iOS 平台不支持开发者使用用户自己的动态 Framework,appstore不能上架,因为 iOS 应用都是运行在沙盒当中,不同的程序之间不能共享代码。但是,iOS 8/Xcode 6 推出之后,因为Extension 和 App 是两个分开的可执行文件,同时需要共享代码,iOS添加了对动态库的支持.

动态库和静态库的区别?

1. 表现形式

静态库:.a和.framework; .a文件是一个纯二进制文件,.framework除了二进制文件还有外部资源文件;.a 文件不能直接使用,至少要有.h文件配合;.framework可以直接使用。

.a+.h+sourceFile = .framework。

动态库:.tbd(系统库)和.framework。

2. 编译链接静态库: 链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。 动态库: 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存

制作一个动态库

1.打开xcode工程

image

2.设置参数在TARGETS下选中工程,在Build Settings下更改几个参数。

image image

(1).Build Active Architecture Only: 指明是否只编译当前连接设备所支持的指令集,如果是,那么只编译出连接设备所对应的指令集,如果否,则编译出所有其它有效的指令集(由Architectures和Valid Architectures决定)。

(2).Dead Code Stripping, 设置为 NO 关闭对代码中“dead”,“unreachable”代码过滤.

(3). Link With Standard Libraries 设置为 NO 避免重复链接.

(4).mach -0 type ,即选择动态库(Dynamic Library)或静态库(Static Library)。

3.编写代码

image

(1) 将头文件需要暴露出去的放入Public

(2) 测试获取bundle中的图片 (同framework, 动态库和静态库的bundle 路径有不同)

image

(3) Embedded Binaries 这个要设置(必要的)

image

4. 接下来就可以编译项目了

(1) 将编译好的framework 可以拖入项目中, 设置好对应的header search path 等

image

(2) 导入以 <> 号形式import导入

5. 可能出现的问题

(1) Headers Not Found

如果Xcode找不到框架的头文件,你可能是忘记将它们声明为public了. 或者项目中search path 的设置问题

(2) 链接警告

第一次编译框架target时,Xcdoe会在链接阶段报告找不到文件夹:

ld: warning: directory not found for option'-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos'

此时,可以clean并重新编译target,警告会消除。

(3) 制作的库支持的CPU指令集不全,所报的错误

image

报错原因:

原来对方用模拟器测试运行的,其CPU架构为x86_64,我导入的framework是真机编译出来的动态库(支持的指令集为armv7、armv7s、arm64,并没有x86_64),所以报此错误,应该生成适合模拟器和真机的通用库。

(4) Core Data momd not found

对于框架项目和应用程序项目,Xcode会以不同的方式编译momd(托管对象模型文件)。Xcode会简单地在根目录创建.mom文件,而不会创建一个.momd目录(目录中包含VersionInfo.plist和.mom文件)。

这意味着,当从一个embedded framework的model中实例化NSManagedObjectModel时,你必需使用.mom扩展名作为model的URL,而不是采用.momd扩展名。

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"MyModel" withExtension:@"mom"];

(5) Unknown class MyClass in Interface Builder file.

由于静态框架采用静态链接,linker会剔除所有它认为无用的代码。不幸的是,linker不会检查xib文件,因此如果类是在xib中引用,而没有在O-C代码中引用,linker将从最终的可执行文件中删除类。这是linker的问题,不是框架的问题(当你编译一个静态库时也会发生这个问题)。苹果内置框架不会发生这个问题,因为他们是运行时动态加载的,存在于iOS设备固件中的动态库是不可能被删除的。

有两个解决的办法:

让框架的最终用户关闭linker的优化选项,通过在他们的项目的Other Linker Flags中添加-ObjC和-all_load。

在框架的另一个类中加一个该类的代码引用。例如,假设你有个MyTextField类,被linker剔除了。假设你还有一个MyViewController,它在xib中使用了MyTextField,MyViewController并没有被剔除。你应该这样做:

在MyTextField中:

  • (void)forceLinkerLoad_ {}

在MyViewController中:

+(void) initialize { [MyTextField forceLinkerLoad_]; }

他们仍然需要添加-ObjC到linker设置,但不需要强制all_load了。

第2种方法需要你多做一点工作,但却让最终用户避免在使用你的框架时关闭linker优化(关闭linker优化会导致object文件膨胀)。

(6) unexpected file type 'wrapper.cfbundle' in Frameworks &Libraries build phase

这个问题发生在把“假”框架项目作为workspace的依赖,或者把它当作子项目时(“真”框架项目没有这个问题)。尽管这种框架项目产生了正确的静态框架,但Xcode只能从项目文件中看出这是一个bundle,因此它在检查依赖性时发出一个警告,并在linker阶段跳过它。

你可以手动添加一个命令让linker在链接阶段能正确链接。在依赖你的静态框架的项目的OtherLinker Flags中加入:

-framework MyFramework

警告仍然存在, 但不会导致链接失败。

(7) Libraries being linked or not being linked into the finalframework

很不幸, “真”框架和“假”框架模板在处理引入的静态库/框架的工作方式不同的。

“真”框架模板采用正常的静态库生成步骤,不会链接其他静态库/框架到最终生产物中。

“假”框架模板采用“欺骗”Xcode的手段,让它认为是在编译一个可重定位格式的目标文件,在链接阶段就如同编译一个可执行文件,把所有的静态代码文件链接到最终生成物中(尽管不会检查是否确实目标代码)。为了实现象“真”框架一样的效果,你可以只包含库/框架的头文件到你的项目中,而不需要包含库/框架本身。

(8)Unrecognized selector in (some class with a category method)

如果你的静态库或静态框架包含了一个模块(只在类别代码中声明,没有类实现),linker会搞不清楚,并把代码从二进制文件中剔除。因为在最终生成的文件中没有这个方法,所以当调用这个类别中定义的方法时,会报一个“unrecognizedselector”异常。

要解决这个,在包含这个类别的模块代码中加一个“假的”类。linker发现存在完整的O-C类,会将类别代码链接到模块。

我写了一个头文件 LoadableCategory.h,以减轻这个工作量:

import "SomeConcreteClass+MyAdditions.h"

import "LoadableCategory.h" MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions); @implementation SomeConcreteClass(MyAdditions)

...

@end

在使用这个框架时,仍然还需要在Build Setting的Other Linker Flags中加入-ObjC。

(9) 执行任何代码前单元测试崩溃

如果你在Xcode4.3中创建静态框架(或库)target时,勾选了“withunit tests”,当你试图运行单元测试时,它会崩溃:

Thread 1: EXC_BAD_ACCESS (code=2, address=0x0) 0 0x00000000 --- 15 dyldbootstrap:start(...)

这是lldb中的一个bug。你可以用GDB来运行单元测试。编辑scheme,选择Test,在Info标签中将调试器Debugger从LLDB改为GDB。

6. 脚本应用

(1) 合并framework


if [ "${ACTION}" = "build" ]

then

INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework

DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework

SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework

if [ -d "${INSTALL_DIR}" ]

then

rm -rf "${INSTALL_DIR}"

fi

mkdir -p "${INSTALL_DIR}"

cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"

#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"

# 使用lipo命令将其合并成一个通用framework 

# 最后将生成的通用framework放置在工程根目录下新建的Products目录下 

lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"

#open "${DEVICE_DIR}"

#open "${SRCROOT}/Products"

fi

(2)瘦身命令样例

mkdir ./bak
cp -r MangoSDK.framework ./bak
lipo MangoSDK.framework/MangoSDK -thin armv7 -output MangoSDK_armv7
lipo MangoSDK.framework/MangoSDK -thin arm64 -output MangoSDK_arm64
lipo -create MangoSDK_armv7 MangoSDK_arm64 -output MangoSDK
mv MangoSDK MangoSDK.framework/

(3) 在打包时候只需要真机环境的脚本

在整个开发过程中,都不再碰到Framework引发的问题。可就在提交App Store时,Xcode报了这个错:

ERROR ITMS-90087: "Unsupported Architecture. Your executable contains unsupported architecture '[x86_64, i386]'."

按字面意思理解,是包里含有不支持的架构。x86_64和i386是模拟器架构,的确是在发布时不需要的。可是Framework是打好的包,如何去除是个问题

大意就是,读取目标app目录,取到Framework目录,遍历之,取出Framework,对其瘦身。

脚本添加后,可以指定在Archive时才使用,以免影响模拟器运行。

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
    FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
    echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
    EXTRACTED_ARCHS=()
    for ARCH in $ARCHS
    do
        echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
        lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
        EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
    done
    echo "Merging extracted architectures: ${ARCHS}"
    lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
    rm "${EXTRACTED_ARCHS[@]}"
    echo "Replacing original executable with thinned version"
    rm "$FRAMEWORK_EXECUTABLE_PATH"
    mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done

相关文章

网友评论

      本文标题:ios 创建动(静)态库

      本文链接:https://www.haomeiwen.com/subject/cntprctx.html