上个星期,突然有一位做flutter开发的小伙伴添加了我的微信,说他的项目中报dyld: Library not loaded: @rpath/App.framework/App这个错误,说是采用我之前的文章Flutter-module嵌入iOS原生老项目中中的方法可以解决,但是他们是团队开发,路径写死对多人开发不够友好,问我有没有其他解决方案。
于是我就打开之前的flutter混合项目,开始探索,删除之前的方法,开始复现dyld: Library not loaded: @rpath/App.framework/App这个错误,如图:
其实项目中报这个错误很常见,这个错误就是dyld去加载App.framework/App这个文件时没有找到,为啥没有找到呢,那就是这个@rpath/App.framework/这个路径下没有App这个文件呗,首先我们得知道这个@rpath代表的是啥,知道@rpath,我们就明白了上面这个报错的原因。
要知道这个@rpath是啥,我们先简单了解一下项目编译到运行的过程中都发生了什么?
- 编译:在我们项目编译后,会生成项目的主程序文件(一个可执行文件Executable),在这个主程序文件所在的目录下,还有info.plish、_CodeSignature、Frameworks等文件,由这些文件组成的一个.app文件,这个文件再进行压缩就形成了我们熟悉的.ipa文件。(不信你可以把Xcode打包一个项目,导出ipa包,修改后缀名为.zip,解压缩看看😄)
- 运行:在项目运行的时候,dyld就会先加载主程序中所依赖的库,我们看下主程序中的Load command,如下图:
图中可以看到,加载系统的库,是没有@rpath的,因为系统的库所在位置是固定的。加载三方库如@rpath/AFNetworking.framework/AFNetworking是App.framework和@rpath/App.framework/App的时候就采用@rpath来代替路径,那么@rpath表示的路径是什么呢?这个@rpath是可以设置的,在我们的项目中,如图:
可看到@rpath是由@executable_path/Frameworks表示的,那么这个@executable_path又是什么呢?它是系统为了方便表示主程序这个可执行文件所在的路径而设计的一个变量,@executable_path表示的是主程序这个可执行文件所在的路径。那么@executable_path/Frameworks也就是@rpath则代表的是主程序文件所在的路径下Frameworks这个文件夹。
搞清楚@rpath是什么这个问题了,那么我们再来看一下.app/Frameworks都有什么?
发现其中确实没有App.framework。
我当时想到的第一种解决方案就是找到flutter_module中的App.framework,然后将它copy到编译后的.app/Frameworks下,不就解决了吗?编译后手动copy也行,当然如果项目clean了,就没有了。我这里采用脚本copy,如图,在Build Phases中新建一个Run Script,将脚本写入即可。
经过一番操作,我这里项目可以顺利运行了,但是这个方式给到咨询我的那个小伙伴,他那里的项目却不行,编译都不过。
然后又经过一系列的查找,最终我把问题定位到flutter的SDK的版本上,因为官方提供的flutter_module嵌入iOS原生项目中第一种方式(pod导入),实际上是使用了podhelper.rb这个脚本来完成一系列的加载,我查看了这个脚本的位置是在flutter的SDK中的,当时想到一个问题就是:我的这个项目21年9月份嵌入的的,我的flutter版本2.2.3也不是最新的,这位咨询的小伙伴的flutter版本也不是最新的,于是,我让他升级一下flutter版本看下,果不其然,他升级flutter后就可以顺利运行了。
其他同学如果遇到这个问题,可以先尝试一下flutter的版本升级,少走一些弯路。
最后贴出解决办法:(评论区小伙伴 ningcol 给出的方法,在此感谢!)
重装Cocoapods和ruby-macho:
sudo gem uninstall ruby-macho sudo gem uninstall cocoapods sudo gem install ruby-macho sudo gem install cocoapods