深入了解替代单纯记忆

本文图片托管在Github上

本文依靠的试验环境:Xcode 13.4.1, Cocoapods 1.11.3

本文首要探寻下Cocoapods是怎么管理不同的library的

阅读本文需求一些前置概念常识:

比方要了解Xcode中project, scheme, target, workspace,还要了解library, framework的概念,能够看下前一篇内容Library, Framework in iOS–Part1

初次履行pod install后Cocoapods会发生ProjectName.xcworkspace,翻开后发现有两个project:ProjectName和Pods

那Cocoapods是怎么将App与Podfile中依靠的library建立依靠关系的呢?

我以一个叫做Test-OC的工程为例来展示,Podfile如下所示:

source 'https://github.com/CocoaPods/Specs.git'
#use_frameworks!
target "Test-OC" do
pod 'lottie-ios'
pod 'MJRefresh'
pod 'AFNetworking', '3.2.0'
end

经过调查工程的Target–Test-OC的依靠关系能够看到:

Test-OC依靠于libPods-Test-OC.a

在Pods这个Project中能够找到Pods-Test-OC这个target以及其他target

  • Pods-Test-OC是一个static library
  • 发现Podfile中依靠的一切library都在Pods的Project中
  • 且每个Pod都是static library

到这里咱们能够了解到:Cocoapods创立了一个Pods-Test-OC.a,然后主工程依靠于该static library

但,现在咱们还不清楚主工程是怎么链接具体的Pod,并且还多了一个问题:

  1. 主工程的Link libraries装备中并没有具体的Pod。主工程仅仅linkPods-Test-OC.a,但Pods-Test-OC这个target中并没有持续去link每一个具体的Pod。所以主工程最终是怎么link具体Pod的
  2. Pods-Test-OCtarget的效果是什么

咱们在主工程的Build Settings找到了答案:

能够看到Other Linker Flags部分增加了对一切Pod的链接指令

原来是在这里链接Pod的,可主工程怎么才干找到每个Pod对应的library呢?

一次偶尔的时机让我大致找到了答案:

在一次履行pod install时,pod给出了一个正告:

[!] The `Test-OC [Debug]` target overrides the `LIBRARY_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-Test-OC/Pods-Test-OC.debug.xcconfig'. This can lead to problems with the CocoaPods installation
- Use the `$(inherited)` flag, or
- Remove the build settings from the target.

意思是我的主工程build settings->library search path的装备覆盖了Pods-Test-OC.debug.xcconfig中library search path的装备。已然这样,那就看下Pods-Test-OC.debug.xcconfig中library search path的内容吧

LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" /usr/lib/swift

所以,主工程是经过上面的路径来寻找具体的Pod对应的library的

至此,咱们大概也就猜到了Cocoapods的工作流程:

  • Cocoapods将每个Pod创立为一个static library的target
  • 构建主工程时,经过修改Other Linker FlagsLibrary Search Path找到每个static library进行链接

至于另一个问题:Cocoapods为什么要创立了一个名为Pods-ProductName的target呢?

其实是为了对一切Pod进行统一装备和管理,比方:

  • 为主工程装备Library Search Path提供数据来历
  • 为每个Pod支撑模块导入能力(modular)

其他

use_frameworks!

默许情况下Cocoapods是将每个Pod都创立为static library的target

Podfile中运用use_frameworks!后,便能够将每个Pod创立为dynamic framework

在Xcode 9之前,Xcode不支撑将用Swift编写的Pod创立为static library。所以开发者不得不运用use_frameworks!。但假如太多的dynamic framework,或许大概率影响到发动速度

PodName-dummy.m

咱们会在每个Pod的目录下都发现一个Cocoapods为它创立的PodName-dummy.m,其间仅创立了一个名为PodsDummy_PodName的空类,为什么这么做?

从前出现过这样一个Apple的bug–Building Objective-C static libraries with categories
:当一个library只有category而没有具体的类时,主工程链接library时会出问题,导致运行时调用相应方法时找不到对应实现

对于该问题有两种解决方案:

  1. 主工程的Other Link Flags中参加-ObjC(其实Cocoapods中也会经过Pods-ProductName这个target的装备文件为主工程参加)
  2. 另一种便是能够在library中创立一个dummy class,主工程中无需初始化该类的目标,就能让链接正确。但个人测验后发现必须初始化目标才干work

所以,已然苹果主张运用-ObjC来解决,一起Cocoapods也增加了该符号,为什么依然要创立dummy class呢?

暂时没搞理解

umbrella header

此部分我想对一个有争议的概念聊一下个人看法

当敞开use_frameworks!时,发现每个pod的目录下都会有一个PodName-umbrella.h文件

我在参考资料中了解到,增加该头文件的首要意图是为了让该pod支撑模块导入(modular)能力,即Cocoapods会为该pod创立一个modular.map文件,其间会用到该文件

本文不会对modular进行具体介绍,简单来讲便是让主工程在运用pod时能够经过@import ModuleName的方法(而非#import <Library/Library.h>)引进,这种方法更好一些,那这个umbrella.h便是一个映射–在遇到@import ModuleName时就会自动相关umbrella.h中的所写的头文件

争议点在于,有人经过这个命名来得出该pod便是一个umbrella framework,我并不认同,因为umbrella framework是嵌套了其他framework的framework,此处明显不是。我认为仅仅一个一般的头文件,仅仅它包含了该pod中一切的公共头文件引进信息,所以用了一个umbrella单词而已

参考

  • Clang Module
  • 系统了解 iOS 库与框架
  • Objective-C categories in static library