记一次podspec踩坑之路

背景:

目前所在的项目组是多媒体开发组,项目开发的场景包括了播放器,特效,视频编辑,视频模板,代码规范工具等多个场景,随着开发迭代,项目已经变得越来越庞大,所有代码放在一个工程中不利于团队开发和维护,于是决定将项目根据功能拆分成不同的库(模块化),各自维护。

iOS平台中使用了cocoapods这个管理工具方便进行库的管理,使用它自制私有库的关键需要自己编写podspec文件(描述库的特定版本信息的文件),于是各种史诗级灾难开始了。

以下是各种灾难现场

image
image
image

作为一个刚才深坑里面爬出来的人,趁着还有一口气在,赶紧记录下这次爬坑经历,给使用podspec的同学做一个参考~

问题解析

问题一

首先我们来看图一,这里报的错误是<cassert>头文件找不到。

c++标准库的头文件找不到

<cassert>是c++标准库里面的一个系统头文件,一般出现这种情况的原因是在Objective-C类型文件中引用了C++的头文件导致的,如果要引用C++的头文件必须将后缀名改成(.mm)即将文件类型改为Objective-C++类型。

然而,目前的问题场景并不是这样的,我们并没有在Objective-C文件中引用c++头文件。目前我们用到了库有两个,一个是渲染库(illusion)和一个播放器库(GDMMCore)。illusion库中有c++文件和Objetcive-C++文件(即封装c++,给外部使用的中间件)和Objective-C文件,GDMMCore库依赖于这个illusion库(即GDMMCore.dependency 'illusion'),我们只有在GDMMCore的Objective-C文件中引用了illusion库的Objective-C文件,如下图所示,就造成了图一的错误。这个是为什么呢?

image

我们沿着引用路径查看,最后定位到了一个叫做illusion-umbrella.h的文件中。

image

如图所示,我们明明只有引用GDCoreGraphics.h(Objective-C类型)这个头文件,为什么引用路径会定位到GDInOutAnimation.h(c++头文件,这个文件里面间接的引用的报错的<cassert>这个头文件)呢?这个illusion-umbrella.h到底又是个什么东西呢?

首先,这个illusion-umbrella.h是因为我们在Podfile中使用了use_frameworks!后由cocopods帮我们自动生成的头文件,这里面导入了所有我们在podspec中配置的共有头文件,有点类似于pch文件的作用,如果不使用use_frameworks!则不会有这个文件。然后这里有个坑,就是只要我们引用了这个文件中导入的头文件(无论是什么类型的头文件),就会引用整个illusion模块,即illusion-umbrella.h中所有的头文件,所以这就造成了上面的引用路径。

这里提到了use_framework!,那么使用和不使用这个有什么区别呢?

image
image

上面两幅图分别是使用了use_framework!和没有使用use_framework!的结果,首先正如我们上面说到的使用了use_framework!之后会生成一个xx.umbrella.h文件。其次,使用use_framework之后最终组件会编译成.framework类型的文件,而不使用的话则是.a类型的文件形式。当然,还不只有这些区别哦~

它还会导致最终的头文件路径不一样,下面两幅图分别是使用了use_framework!和没有使用最终头文件的路径。

img
img

那么这个信息有什么用呢?接下来我就介绍一下我是怎么解决第一个问题的,正如上面提到的,问题在于引用了xx.umbrella.h中的文件就会引用整个umbrella.h中的所有头文件(这些头文件中包含了c++文件)。

第一个思路,不使用use_framework!就不会生成umbrella.h这个文件,也就不会导致引用整个模块的头文件,也就不会有图一的问题了。但是在Podfile中使不使用use_framework!是由业务方(即需要用这个库的同学)自己决定的,我们作为服务方(即提供组件的同学)当然不能限制使用的同学要这样做,毕竟我们的目标是要让业务方同学用起来方便,爽。而这种方案是对业务方同学做了一个很大的限制,方案pass。

第二个思路,上面我们说到了umbrella.h里面引入的是所有的我们在podspec中配置的公开(public)的头文件,也就是说私有的(private)头文件不会出现在这里面,那么我们可以将c++文件配置成私有的头文件,这样的话即使引用了整个umbrella.h里面的头文件也不会引用到c++头文件,方案可行,配置如下图所示。

image

综上,方案二能够解决图一引用c++文件的问题,但是以方案二执行之后又导致了另一个问题,就是由于把文件设置成了私有导致了GDMMCore库中找不到c++文件,无法使用,这个问题又要怎么处理呢?

这个我们可以通过在Build Setting中设置Header Search Paths,这样编译器就能根据我们配置的路径去找到私有的头文件了,如下图所示。

image

那么头文件的路径如何设置呢?又该如何在podspec中配置这些呢?让我们回到podspec中,看看具体的配置代码:

 search_paths = [
    #Podfile不使用use_frameworks搜索路径
    '$(PODS_ROOT)/Headers/Public/illusion',
    '$(PODS_ROOT)/Headers/Private/illusion',
    #Podfile使用use_frameworks库内搜索路径
    '$(PODS_ROOT)/illusion/Headers',
    '$(PODS_ROOT)/illusion/PrivateHeaders',
    #Podfile使用指定路径链接
    '$(PODS_TARGET_SRCROOT)/src',
    '$(PODS_TARGET_SRCROOT)/src/animation/inout',
    '$(PODS_TARGET_SRCROOT)/src/animation/transition',
    '$(PODS_TARGET_SRCROOT)/src/entity',
    '$(PODS_TARGET_SRCROOT)/src/filter',
    '$(PODS_TARGET_SRCROOT)/src/math',
    '$(PODS_TARGET_SRCROOT)/src/stb',
    '$(PODS_TARGET_SRCROOT)/src/util',
    ]
    private_header_path = [
    '${PODS_CONFIGURATION_BUILD_DIR}/illusion/illusion.framework/PrivateHeaders',
    '$(PODS_ROOT)/Headers/Private/illusion',
    ]
    s.pod_target_xcconfig = {
        'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES' ,
        'HEADER_SEARCH_PATHS' => search_paths.join(' '),
    }
    s.user_target_xcconfig = {
      'HEADER_SEARCH_PATHS' => private_header_path.join(' '),
    }

代码中我们可以看到一个关键key值 'HEADER_SEARCH_PATHS',这个对应的就是头文件搜索路径了。我们可以看到代码中有两个地方都进行配置了,分别是pod_target_xcconfig和user_target_xcconfig,那么这两个有什么区别呢?

pod_target_xcconfig设置的是当前库的Build Settings,这里对应的是illusion库。user_target_xcconfig设置的是project中的Build Settings,即在运行的target中的Build Settings。这个在Cocoapods的官网上不建议使用,上面说设置这个有可能会和工程中本身的设置造成冲突。这里因为我们这个库有可能被工程中直接使用,c++头文件被设置为私有头文件,所以这里进行了配置。

接下来我们看下头文件的路径,如代码所示,分为三种情况:

1.使用了use_frameworks,此时的头文件在framwork中寻找,公有的头文件在$(PODS_ROOT)/Headers/Public/库名中,私有的头文件在对应的Private/库名中。

2.不使用use_frameworks,此时的头文件在Pods文件夹对应库中的Headers(公有)和PrivateHeaders(私有)文件夹中。

3.还有一种情况是在Podfile中是使用本地库的方式引用,即通过path = > '../'的方式,此时的头文件路径会到本地工程中对应的路径中寻找。

根据以上不走配置好podspec之后,我们执行pod install,再次编译发现引用c++头文件的问题解决了。

问题二

image

这里报的错误是头文件找不到,我们发现这里引用头文件的方式是通过相对路径去寻找的,使用相对路径的前提是必须能够找到当前所在的目录层级,于是还是像上面一样,我们需要把当前的目录层级配置到HEADER_SEARCH_PATHS中。

  search_paths = [
    #Podfile使用指定路径链接
    '$(PODS_TARGET_SRCROOT)/source',
    '$(PODS_TARGET_SRCROOT)/source/common',
    '$(PODS_TARGET_SRCROOT)/source/module/media',
    ]
    s.pod_target_xcconfig = {
      'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
      'HEADER_SEARCH_PATHS' => search_paths.join(' '),
    }

再次pod install之后这个问题也解决了。

问题三

image

图中的问题信息说的是GDSizeFormatToVideo这个函数在两个模块中有不同的定义,但是搜索了整个工程中对于这个函数的定义只存在于illusion库中的GDCoreGraphics.h中,我们找到报错的引用路径看看

image

如果所示,我们的引用方式用的是<GDMMCore/GDTimelineInfo.h>的方式找到GDTimelineInfo.h,而GDTimelineInfo.h中间接的引用到了GDCoreGraphics.h这个头文件,这里有一个坑就是用这种方式引用的话,编译器会认为这里间接引用到的GDCoreGraphics.h这个文件是属于GDMMCore中的文件,所以这样就造成了编译器认为在GDMMCore和ilusion两个库中都存在这个GDCoreGraphics.h头文件,以及头文件中的函数,也就造成了图中所示的错误。

这个问题的解决方式比较简单,我们只需要更改引用文件的方式,不使用<模块/文件>的方式引用,而是直接使用"文件"的方式,这样就解决了这个问题。

image

以上就是我这次的podspec踩坑经历,如果有遇到类似问题的同学可以参考一下,也可以和我交流哈~

参考链接:

cocoapods

Xcode Build Settings

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,693评论 0 3
  • 起因 理论功底 动态库和静态库 介绍 静态库和动态库的区别 举个例子, iOS 项目中使用 Embeded Fra...
    leverkusen188阅读 1,009评论 0 3
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,089评论 1 32
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,176评论 0 9
  • 要解除循环依赖,引入包管理技术cocoapods会让我们更有效率。pod不允许组件间有循环依赖,若有pod ins...
    cs_mark阅读 2,060评论 0 1