学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

iOS Pod私有库发布

为了提高项目中代码的复用,通常会把耦合度低的模块解耦出来做成Pod库放在私有仓库上,很多To B的业务,需要制作SDK给第三方使用,这时候需要提供静态framework给第三方。

静态库生成

模块说明书PodSpec

我们把podspec文件上传给cocoapod的master上,当别人配置好CocoaPods就会把我们的podspec.json下载到/.cocoapods/repos/master/下,用户要使用AFNetworking查找本地/.cocoapods/repos/master/ 找到这个AFNetworking.podspec.json文件根据内容下载配置。

podspec文件就是充当了一个我们源代码模块的说明书,告诉开发者我们模块名称,我们用途,可以在哪下载源代码 ,需要如何配置。

podspec创建

pod 还提供了一个一条龙服务的命令:pod lib create 会帮我们创建一个跟项目名称相同podspec文件 测试project 工程 测试框架。

1
pod lib create XXXX

podspec常用属性说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

Pod::Spec.new do|s|
//项目名
s.name ='Project'
//版本号
s.version ='1.0.1'
//listen文件的类型
s.license = 'MIT'
//简单描述
s.summary = 'ATest in iOS.'
//项目的getub地址,只支持HTTP和HTTPS地址,不支持ssh的地址
s.homepage ='https://github.com/3671932/Project'
//作者和邮箱
s.authors = {'MMMMM' => 'XXXXXX@qq.com' }
//git仓库的https地址
s.source = { :git=> 'https://github.com/3671932/Project.git', :tag =>s.version}
//是否要求arc(有部分非arc文件情况未考证)
s.requires_arc = true
//在这个属性中声明过的.h文件能够使用<>方法联想调用(这个是可选属性)
s.public_header_files = 'UIKit/*.h'
//表示源文件的路径,这个路径是相对podspec文件而言的。(这属性下面单独讨论)
s.source_files ='AppInfo/*.*'
//需要用到的frameworks,不需要加.frameworks后缀。(这个没有用到也可以不填)
s.frameworks ='Foundation', 'CoreGraphics', 'UIKit'

//图片资源可支持xcassets
s.resource_bundles = {
'WSFBaseService' => ['WSFBaseService/Assets/WSFBaseService.xcassets']
}

//建立名称为Info的子仓库(虚拟路径)
s.subspec 'Info' do |ss|
....
end

end

模块说明书的验证

1
pod spec lint test.podspec 

可以设置 –allow-warnings来忽略警告,通过–verbose查看错误具体信息

模块说明书注意

一个tag对应一个版本 都会包含一个podspec 文件

仓库Repo

存放说明书的地方就是仓库
创建自己cocoapod仓库的命令就是

1
2
3
//REPO_NAME 仓库名称
//SOURCE_URL 仓库远程地址
pod repo add REPO_NAME SOURCE_URL

推送podspec需要添加仓库名称和 podspec文件地址

1
pod repo push RooboSpecs xxxxxx.podspec

编译SDK静态库

如果需要隐藏源代码,可以把pod库编译成静态库的方式。
可以通过安装cocoapod的编译插件配合我们的cocoapod 文件来编译我们静态库文件 安装cocoapods-packager

1
sudo gem install cocoapods-packager
  • 编译库文件
1
2
//pod package NAME [SOURCE]
pod package Roobo_Plus.podspec --force --embedded
  • Pod Package命令含义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 //强制覆盖之前已经生成过的二进制库 
--force

//生成静态.framework
--embedded

//生成静态.a
--library

//生成动态.framework
--dynamic

//动态.framework是需要签名的,所以只有生成动态库的时候需要这个BundleId
--bundle-identifier

//不包含依赖的符号表,生成动态库的时候不能包含这个命令,动态库一定需要包含依赖的符号表。
--exclude-deps

//表示生成的库是debug还是release,默认是release。--configuration=Debug
--configuration


--no-mangle
//表示不使用name mangling技术,pod package默认是使用这个技术的。我们能在用pod package生成二进制库的时候会看到终端有输出Mangling symbols和Building mangled framework。表示使用了这个技术。
//如果你的pod库没有其他依赖的话,那么不使用这个命令也不会报错。但是如果有其他依赖,不使用--no-mangle这个命令的话,那么你在工程里使用生成的二进制库的时候就会报错:Undefined symbols for architecture x86_64。

--subspecs

//如果你的pod库有subspec,那么加上这个命名表示只给某个或几个subspec生成二进制库,--subspecs=subspec1,subspec2。生成的库的名字就是你podspec的名字,如果你想生成的库的名字跟subspec的名字一样,那么就需要修改podspec的名字。
这个脚本就是批量生成subspec的二进制库,每一个subspec的库名就是podspecName+subspecName。

--spec-sources

//一些依赖的source,如果你有依赖是来自于私有库的,那就需要加上那个私有库的source,默认是cocoapods的Specs仓库。--spec-sources=private,https://github.com/CocoaPods/Specs.git。

注意:
使用–library编译出的.a静态库文件,pod package插件有问题的,根部不会生成头文件。所以只能编译framwork的库文件

静态库制作遇到的坑

运行时便无法找到对应的selector?

编译/链接过程

  • 编译器在编译的过程中首先会将 .c/.cc/.cpp/.m/.mm 的源文件编译成后缀为 .o 的对象文件 ,源文件与对象文件是一一对应的,对象文件中包含了符号、代码以及数据,对象文件是不能被系统加载并使用的。当在编译生成静态库时,这些所有的对象文件,都会被封装到 .a(archive) 文件,可以理解为一个归档文件,也就是我们平常使用的静态库。

  • 当需要生产二进制文件或是动态库时,编译器会对静态库 .a(archive) 文件进行处理;编译器将获取静态库中所有的符号表,并检查哪些符号被引用,只有被引用的对象文件才会被链接器真正的加载并处理。例如在静态库中有 10 个对象文件,但被引用到只有 2 个,则链接器只加载被引用的 2 个对象文件,未被使用到的 8 个对象文件则会被忽略。

  • 在 C/C++ 语言中,这种机制可以很好的工作,因为 C/C++ 语言会尽可能的在编译期去做这些事。在 Objective-C 语言中非常依赖运行时特性,Category 就是基于运行时实现,但它并不会像类或者函数一样被创建链接符号,编译器在检查符号表时便不能检查到 Category 对应的符号表,从而 Category 的对象文件就不能被正常的加载,在运行时便无法找到对应的 selector 。

解决方案

  1. Other Linker Flags 添加
  • -all_load 告诉编译器 对于所有静态库中的所有对象文件,不管里面的符号有没有被用到,全部都载入,这种方法可以解决问题,但是会产生比较大的二进制文件

  • -force_load 并指定路径,-force_load $(BUILT_PRODUCTS_DIR)/<library_name.a> 这种方法和 -all_load 类似,不同的是它只载入指定的静态库

  • -ObjC 如果在静态库的对象文件中发现了 Objective-C 代码,就把它载入,Category 中肯定会存在 Objective-C 代码。

  1. 在 Category 的源文件里添加 Fake symbol,并确保以某种方法在编译时引用了该 Fake symbol,这会使得 Fake symbol 对象文件被加载时它里面 Category 代码也会被载入。该方法可以控制哪些 Category 可以被正常加载,同时也不需要添加编译参数做特殊处理。

参考

  1. iOS 静态库中的 Category 运行时错误
  2. podspec文件解析
  3. Pod 库的资源引用