一. 静态库符号冲突解决
1.1 链接同名静态库会不会冲突?
首先打开第一份工程LGTestApp
,导入了AFNetworking
库
platform :ios, '14.0'
target 'LGTestApp' do
# use_frameworks!
pod 'AFNetworking'
end
接下来往工程中Frameworks
目录下导入AFNetworking_static
目录下的libAFNetworking.a
温馨小提示:
把Pods/Pods-LGTestApp.debug.xcconfig
配置文件中的
OTHER_LDFLAGS = $(inherited) -all_load -l"AFNetworking"
工程链接静态库中所有代码
改成默认OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking"
,工程链接静态库的方式会变成,链接所有与OC相关的类或者分类到当前工程ipa包中。
此时cocoapods链接了一个AFNetworking
库,手动又拖进去一个AFNetworking
库,Cmd + R会不会报冲突?
不会报错,cocoapods正常通过三要素进行链接,第一点通过HEADER_SEARCH_PATHS
找到api,第二点通过LIBRARY_SEARCH_PATHS
找到路径,最后通过OTHER_LDFLAGS
根据库的名称链接库。当把一个库拖入Frameworks
目录下,此时链接库的方式是通过静态库的路径进行链接。相当于OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" /Users/wn/Documents/资料/上课代码/01-静态库冲突原因/LGTestApp/libAFNetworking.a
,这个时候链接器发现静态库同名,会优先链接第一个库,第二个库就不会链接。
1.2 链接同名静态库报冲突的情况?
修改AFNetworking_static
目录下的libAFNetworking.a
名称为libAFNetworking2.a
,导入Frameworks
目录下,链接器会认为是两个不同的静态库,这个时候相同的静态库起不同的名字就会冲突。此时报了duplicate symbols for architecture x86_64
符号冲突,重复定义符号。
OC为什么是一门动态语言?
当把代码编译到MachO之后,MachO中有一个__Data段,这个段的__Object_section中存放着所有与类相关的内容。当把MachO加载到内存时,Runtime会把section读出来,生成相应的Class_RO,Class_RW结构体,动态化的初始化对象。
下面我们创建一个单符号冲突的情况?
创建AFNetworking2
静态库,静态库内容如下
// AFNetworking.h内容
#import <Foundation/Foundation.h>
void global_function() {
}
@interface AFURLSessionManager : NSObject
@property (readonly, nonatomic, strong) NSURLSession *session;
@end
// AFNetworking.m内容
#import "AFNetworking.h"
@implementation AFURLSessionManager
@end
创建完静态库之后,把静态库工程添加到LGTestApp

把创建的静态库AFNetworking2
添加到Frameworks下

编译之后报错如下

- 命令一查看符号
$ tty
/dev/ttys003
// 修改AFNetworking2 静态库中的RunCMD.Config.xcconfig内容如下
// 详细输出终端命令
VERBOSE_SCRIPT_LOGGING=-v
MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/libAFNetworking2.a
MAP_PATH=${SRCROOT}/cat.m
CMD = nm -pa ${MAP_PATH}
TTY=/dev/ttys003
AFNetworking2
静态库中RunCMD.Config.xcconfig
配置完成之后,Cmd + R 运行,终端打印出符号如下

S
表示存放在其他session,也就是__Data __ObjC的session段中.
- 命令二查看符号,配置
RunCMD.Config.xcconfig
内容如下
// 详细输出终端命令
VERBOSE_SCRIPT_LOGGING=-v
MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/libAFNetworking2.a
MAP_PATH=${SRCROOT}/cat.m
CMD = objdump --macho -t ${MACH_PATH}
TTY=/dev/ttys003
Cmd + R 运行,终端打印出符号如下

小结
- 只有全局符号才会产生冲突
解决方法
- 方法一 文件产生冲突,可以使用
ar
命令对静态库进行解压缩,去掉重复符号,再包装成静态库 - 方法二 符号产生冲突,上面产生冲突的符号
AFURLSessionManager
在不同文件中
1.如果能够获取到源码,直接修改其中一个符号名称即可解决,如CT_AFURLSessionManager
2.如果获取不到源码,可以对单独的库指定一些参数,如OTHER_LDFLAGS = $(inherited) -force_load "库路径" -l"AFNetworking"
,如果库比较多的话,需要对每一个库进行指定,虽然能解决问题,但是比较麻烦
3.获取不到源码,推荐使用objcopy
,直接对冲突的符号添加前缀或后缀,以此修改符号
$ llvm-objcopy --prefix-symbols=Cat...

我们发现llvm-objcopy
命令并不支持MachO文件格式,可以使用下面命令
$ llvm-objcopy --redefine-sym "_OBJC_CLASS_$_AFURLSessionManager"="Cat__OBJC_CLASS_$_AFURLSessionManager"...


成功修改第一个符号的前缀,接下来只需要把后面两个符号前缀修改,即可解决符号冲突
$ nm -gUAj .a路径
显示.a文件内所有的全局符号

此时把libAFNetworking2.a
导入Frameworks
目录下,Cmd + B编译成功
二. 解决符号冲突原因&脚本
上面我们把符号_OBJC_CLASS_$_AFURLSessionManager
改成了Cat_OBJC_CLASS_$_AFURLSessionManager
,Cat_OBJC_CLASS_$_AFURLSessionManager
已经不属于OC的符号了,因为前缀发生了变化,底层已经解析不出来了。所以OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking"
链接器在去链接AFNetworking2
的时候,发现不是OC的符号,编译器会把Cat_OBJC_CLASS_$_AFURLSessionManager
strip掉。

搜索可执行文件LGTestApp
带Cat
的符号,并没有找到,确定编译器把Cat_OBJC_CLASS_$_AFURLSessionManager
strip掉了

修改OTHER_LDFLAGS = $(inherited) -all_load -l"AFNetworking"
发现定义的全局符号global_function
以及包含Cat
的符号都存在。
思考一下
上面的静态库AFNetworking2
中符号比较少,只报了三个符号冲突,当冲突的符号比较多时,我们不能一个个的添加前缀或后缀,那么该怎么解决呢?
- 方法一 首先查看
libAFNetworking.a
静态库的全局符号
$ nm -gUAj libAFNetworking.a路径

发现每个符号所在路径并不相同,我们只需要对符号拼接前缀,放入原本的路径即可,所以我们可以编写脚本使用正则表达式
来实现
// main.py脚本内容如下
import subprocess
import re # 正则相关的方法
# path表示路径,使用nm命令把该路径的静态库符号输出
def getSymbols(path):
# 在当前进程中执行命令输出静态库的符号
# f表示格式化字符串
return subprocess.getoutput(f'nm -gUAj {path}')
# 也可以这样写 return subprocess.getoutput("nm -gUAj {}".format(path))
# 修改完成之后输出到新的路径,把上图中 ‘ ___destory_helper_block_e8_32s’前面的路径设置为空,这里只需要替换一下
def writeNewSymbolsToFile(syms, path, newPath)
# r表示单引号里面是一个正则表达式,下面的正则表达式就能完整匹配到‘ ___destory_helper_block_e8_32s’前面的路径
newStr = re.sub(rf'{path}[:](.+)(.o: )', '', syms)
# 把上面字符串按照换行符分割成数组
lines = newStr.splitlines()
newSys = ''
for s in lines:
# 去掉字符串中的换行符
word = s.strip('\n')
newSys += word + ' ' + f'_Cat{word}' + '\n'
print(newSys)
# 打开newPath路径,默认创建w文件
with open(newPath, 'w') as file:
# 文件写入newPath
file.write(newSys)

_OBJC_CLASS_$_AFAutoPurgingImageCache _Cat_OBJC_CLASS_$_AFAutoPurgingImageCache
_OBJC_CLASS_$_AFCachedImage _Cat_OBJC_CLASS_$_AFCachedImage
_OBJC_IVAR_$_AFAutoPurgingImageCache._cachedImages _Cat_OBJC_IVAR_$_AFAutoPurgingImageCache._cachedImages
...
我们发现符号名称成功修改,此时Cmd + B编译成功
- 方法二 现在是两个不同名字,相同符号的静态库,可以把一个静态库改成动态库,就可以解决符号冲突的问题
platform :ios, '14.0'
target 'LGTestApp' do
use_frameworks!
pod 'AFNetworking'
end
三. Git集中式开发
首先我们来创建一个远程仓库,桌面创建目录远程仓库
并进入,在远程仓库
目录下创建两个目录CT.Remote
HK.Remote
// 进入CT.Remote目录,初始化远程仓库
$ cd /Users/wn/Desktop/远程仓库/CT.Remote
// 初始化Git仓库,初始化完成之后,其他地方的代码就可以push到这个仓库
$ git init --bare
Initialized empty Git repository in /Users/wn/Desktop/远程仓库/CT.Remote/
// 进入HK.Remote目录,初始化远程仓库
$ cd /Users/wn/Desktop/远程仓库/HK.Remote
$ git init --bare
Initialized empty Git repository in /Users/wn/Desktop/远程仓库/HK.Remote/
接下来在桌面创建集中式开发
目录,并在里面分别创建两个目录CT
HK
,在CT
目录里面创建空工程LGClass
// LGClass工程中的ViewController.swift类中打印几句话
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Hello Cat!")
print("Hello Dog!")
}
}
然后使用git命令分别初始化环境
$ cd /Users/wn/Desktop/集中式开发/CT
$ git init
Initialized empty Git repository in /Users/wn/Desktop/集中式开发/CT/.git/
// 查看是否添加过远程仓库
$ git remote
// 在当前目录添加一个远程仓库,origin给远程仓库起别名
$ git remote add origin /Users/wn/Desktop/远程仓库/CT.Remote
// 查看到远程仓库origin
$ git remote
origin
// 再添加一个远程仓库
$ git remote add originHK /Users/wn/Desktop/远程仓 库/HK.Remote
// 查看到有两个远程仓库,意味着当前CT目录下的代码可以往push到两个远程仓库
$ git remote
origin
originHK
// 添加到暂存区并提交代码
$ git add .
$ git commit -m 'init commit'
[master (root-commit) a4ca154] init commit
16 files changed, 730 insertions(+)
create mode 100644 LGClass/LGClass.xcodeproj/project.pbxproj
create mode 100644 LGClass/LGClass.xcodeproj/project.xcworkspace/contents.xcworkspacedata
create mode 100644 LGClass/LGClass.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
create mode 100644 LGClass/LGClass.xcodeproj/project.xcworkspace/xcuserdata/wn.xcuserdatad/UserInterfaceState.xcuserstate
create mode 100644
...
// 查看当前分支
$ git branch
* master
// push到指定的远程仓库,下面报错提示没有指定push到远程仓库的哪个分支
$ git push origin
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin master
// push到origin远程仓库的master分支
$ git push --set-upstream origin master
Enumerating objects: 32, done.
Counting objects: 100% (32/32), done.
Delta compression using up to 4 threads
Compressing objects: 100% (28/28), done.
Writing objects: 100% (32/32), 23.48 KiB | 3.35 MiB/s, done.
Total 32 (delta 2), reused 0 (delta 0)
To /Users/wn/Desktop/远程仓库/CT.Remote
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
// 进入远程仓库查看提交
$ cd /Users/wn/Desktop/远程仓库/CT.Remote
$ git log
commit a4ca154e8956919d3ee63392788258859afc3dc8 (HEAD -> master)
Author: wn <chris.wang@pingcoo.com>
Date: Sat May 8 22:25:07 2021 +0800
init commit
// 提交到远程仓库originHK的master分支
$ cd /Users/wn/Desktop/集中式开发/CT
$ git push --set-upstream originHK master
Enumerating objects: 32, done.
Counting objects: 100% (32/32), done.
Delta compression using up to 4 threads
Compressing objects: 100% (28/28), done.
Writing objects: 100% (32/32), 23.48 KiB | 2.94 MiB/s, done.
Total 32 (delta 2), reused 0 (delta 0)
To /Users/wn/Desktop/远程仓库/HK.Remote
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'originHK'.
现在想要在集中式开发 -> HK
目录下也提交代码,操作如下
$ cd /Users/wn/Desktop/集中式开发/HK
$ git init
Initialized empty Git repository in /Users/wn/Desktop/集中式开发/HK/.git/
$ git remote add -t master origin /Users/wn/Desktop/远程仓库/CT.Remote
// 成功从远程仓库origin中拉下来代码
$ git pull
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 32 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (32/32), done.
From /Users/wn/Desktop/远程仓库/CT.Remote
* [new branch] master -> origin/master
Fast-forwrod merge:
CT
目录下成功拉下来LGClass
工程代码,现在修改代码如下
// LGClass工程中的ViewController.swift类中打印几句话
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Hello Cat!")
print("Hello Dog!")
print("Hello Tiger!")
}
}
$ cd /Users/wn/Desktop/集中式开发/CT
$ git add .
$ git commit -m 'add tiger'
[master 122e218] add tiger
2 files changed, 1 insertion(+), 5 deletions(-)
rewrite LGClass/LGClass.xcodeproj/project.xcworkspace/xcuserdata/wn.xcuserdatad/UserInterfaceState.xcuserstate (76%)
// 成功提交一次
$ git push
Enumerating objects: 19, done.
Counting objects: 100% (19/19), done.
Delta compression using up to 4 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (10/10), 6.14 KiB | 3.07 MiB/s, done.
Total 10 (delta 4), reused 0 (delta 0)
To /Users/wn/Desktop/远程仓库/HK.Remote
a4ca154..122e218 master -> master
// 查看远程提交记录,发现远程master分支记录线条是直的,这就是单人开发Fast-forwrod merge形式
$ git log --oneline --decorate --graph --stat
* 761a65c (HEAD -> master) add tiger
| .../UserInterfaceState.xcuserstate | Bin 13336 -> 16644 bytes
| LGClass/LGClass/ViewController.swift | 6 +-----
| 2 files changed, 1 insertion(+), 5 deletions(-)
* a4ca154 (origin/master) init commit
LGClass/LGClass.xcodeproj/project.pbxproj | 344 ++++++++++++++++++++
.../project.xcworkspace/contents.xcworkspacedata | 7 +
.../xcshareddata/IDEWorkspaceChecks.plist | 8 +
.../UserInterfaceState.xcuserstate | Bin 0 -> 13336 bytes
.../ws.xcuserdatad/UserInterfaceState.xcuserstate | Bin 0 -> 15456 bytes
.../xcschemes/xcschememanagement.plist | 14 +
.../xcschemes/xcschememanagement.plist | 14 +
LGClass/LGClass/AppDelegate.swift | 36 ++
.../AccentColor.colorset/Contents.json | 11 +
.../AppIcon.appiconset/Contents.json | 98 ++++++
LGClass/LGClass/Assets.xcassets/Contents.json | 6 +
.../LGClass/Base.lproj/LaunchScreen.storyboard | 25 ++
LGClass/LGClass/Base.lproj/Main.storyboard | 24 ++
LGClass/LGClass/Info.plist | 66 ++++
LGClass/LGClass/SceneDelegate.swift | 52 +++
LGClass/LGClass/ViewController.swift | 25 ++
16 files changed, 730 insertions(+)
// 也可以用glola的全写形式查看提交记录,发现远程master分支记录线条是直的,这就是单人开发Fast-forwrod merge形式
$ git log --graph --pretty='%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --all
* 122e218 - (HEAD -> master, originHK/master) add tiger (15 minutes ago) <wn>
* a4ca154 - (origin/master) init commit (54 minutes ago) <wn>


- Fast-forwrod merge: 绿色表示远程分支,蓝色表示提交到远程的本地分支,push的时候这俩分支需要进行合并,这时发现远程分支后面并没有新的提交,那么直接把本地提交的代码合并到远程分支后面,这就是Fast-forwrod merge
多人开发
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Hello Cat!")
print("Hello Dog!")
print("Hello Elephant!")
}
}
$ cd /Users/wn/Desktop/集中式开发/HK
$ git add .
$ git commit -m 'add Elephant'
[master bd5950d] add Elephant
2 files changed, 5 insertions(+), 1 deletion(-)
rewrite LGClass/LGClass.xcodeproj/project.xcworkspace/xcuserdata/wn.xcuserdatad/UserInterfaceState.xcuserstate (94%)
$ git log --oneline --decorate --graph --stat
* bd5950d (HEAD -> master) add Elephant
| .../UserInterfaceState.xcuserstate | Bin 16644 -> 16909 bytes
| LGClass/LGClass/ViewController.swift | 6 +++++-
| 2 files changed, 5 insertions(+), 1 deletion(-)
* a4ca154 (origin/master) init commit
LGClass/LGClass.xcodeproj/project.pbxproj | 344 ++++++++++++++++++++
.../project.xcworkspace/contents.xcworkspacedata | 7 +
.../xcshareddata/IDEWorkspaceChecks.plist | 8 +
.../UserInterfaceState.xcuserstate | Bin 0 -> 13336 bytes
.../ws.xcuserdatad/UserInterfaceState.xcuserstate | Bin 0 -> 15456 bytes
.../xcschemes/xcschememanagement.plist | 14 +
.../xcschemes/xcschememanagement.plist | 14 +
LGClass/LGClass/AppDelegate.swift | 36 ++
.../AccentColor.colorset/Contents.json | 11 +
.../AppIcon.appiconset/Contents.json | 98 ++++++
LGClass/LGClass/Assets.xcassets/Contents.json | 6 +
.../LGClass/Base.lproj/LaunchScreen.storyboard | 25 ++
LGClass/LGClass/Base.lproj/Main.storyboard | 24 ++
LGClass/LGClass/Info.plist | 66 ++++
LGClass/LGClass/SceneDelegate.swift | 52 +++
LGClass/LGClass/ViewController.swift | 25 ++
16 files changed, 730 insertions(+)
// 此时push到远程会报错,提示我们远程分支有了新的提交,在push之前要先git pull 下来代码
$ git push --set-upstream origin master
![rejected] master -> master (fetch first)
error: failed to push some refs to /Users/wn/Desktop/远程仓库/HK.Remote
hint: Updates were rejected because the remote contains work that you do
hint: (e.g.,’git pull …’) before pushing again
...
// 拉代码的时候产生了冲突
$ git pull origin master
From /Users/wn/Desktop/远程仓库/HK.Remote
* branch master -> FETCH_HEAD
Auto-merging LGClass/LGClass/ViewController.swift
CONFLICT (content): Merge conflict in LGClass/LGClass/ViewController.swift
Automatic merge failed; fix conflict and then commit the result.
// 此时产生了冲突
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Hello Cat!")
print("Hello Dog!")
<<<<<<< HEAD
print("Hello Tiger!")
=======
print("Hello Elephant!")
>>>>>>> CommitID
}
}
// 手动解决完上面冲突之后提交代码
$ git commit -m 'new merge'

- git pull 有两个命令,一个git fetch 一个git merge
- git fetch 之后本地有两个master分支,一个远程一个本地。

// 此时查看远程提交记录,发现远程master分支多了个分支记录
$ git log --oneline --decorate --graph --stat
// 下面我们来把上面的merge多出来的线条撸直
// 回到commit之前的状态,也就是上一个HEAD
$ git rev-parse ORIG_HEAD
761a65cbef6d7bbe6403c3c7aac5051eb70ecc93
// 这时就恢复到git pull拉取远程分支之前的状态
$ git reset --hard ORIG_HEAD
// 表示当前分支要添加到远程分支后面
$ git pull origin master --rebase
// 查看冲突解决冲突
$ git mergetool
// 添加到暂存区,并且添加到远程分支后面
$ git add .
$ git rebase --continue
// 此时查看远程提交记录,发现远程master分支成功撸直
$ git log --oneline --decorate --graph --stat
// 成功解决
$ git push
// 举例
// 表示当前分支与dog分支进行合并,merge与rebase刚好相反
$ git merge dog







- rebase 不是创建一个新的commit,而是将指定的分支放到另一个分支的结尾
小提示
分支上面保存的是最后一次提交,并不是所有的提交,branch本质上表示当前分支的最后一次提交,本地的dev分支提交到远程的dev分支,会发生一次合并
网友评论