iOS动态库导致的单例无效问题

这里的单例无效,指的是虽然用了singleton模式,本该在APP生命周期内,只有一个单例对象,一个内存地址,却出现多个对象的问题。如下是singleton模式代码:

@implementation AObject

+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    static AObject *instance;
    dispatch_once(&onceToken, ^{
        instance = [AObject new];
        NSLog(@"AObject: %p", instance);
        instance.name = @"A";
    });
    return instance;
}
@end

基本上通过[AObject shareInstance],在工程任何地方取得的对象,都是同一个内存地址的单例对象。但发现某种存在动态库情况下,出现[AObject shareInstance]取到多个不同对象的情况。

单例无效demo测试

有个简单的测试demo

目录结构:
.
├── LA0
├── LB0
├── LC0
├── LC1
├── MainProj
└── Test.xcworkspace
工程结构:
MainProj
------------------
LC0    |    LC1
------------------
LB0
------------------
LA0
------------------

Test workspace中,MainProj是主工程,LC0、LC1、LB0、LA0都是依赖的framework。如图示,MainProj依赖LC0、LC1;LC0、LC1依赖LB0;LB0依赖LA0。但在build phases中,上层的link binary with libraries不仅将直属下层加入,同时也将下层的下层加入。例如LC0的配置:


WX20180306-093802@2x.png

单例类AObject在LA0中,上层库、主工程都会调用单例对象。

1. 所有framework都是static library

设置LC0、LC1、LB0、LA0的Build Settings->Mach-O Type->static library。
运行工程(注:运行前需要清空products文件夹下之前编译好的framework、.app,保证环境clear),console:

2018-03-01 16:06:31.850360+0800 MainProj[24755:2092911] AObject: 0x6000000123d0

单例对象[AObject shareInstance]只有一个内存地址。单例模式没有问题。

2. 设置LA0为dynamic library,其他framework都是static library

2018-03-01 16:08:29.750429+0800 MainProj[24800:2095054] AObject: 0x600000012a80

单例模式没有问题

3. 设置LB0为dynamic library,其他framework都是static library

objc[50214]: Class AObject is implemented in both /Users/hotacool/Library/Developer/Xcode/DerivedData/Test-fsrnousjgrbqxngqgmulsskbrcln/Build/Products/Debug-iphonesimulator/LB0.framework/LB0 (0x10daf6418) and /Users/hotacool/Library/Developer/CoreSimulator/Devices/E925C3DF-D46D-4DCF-9936-9239176C4189/data/Containers/Bundle/Application/BA8871EE-BFBA-4F94-9A05-EE56FF8B5758/MainProj.app/MainProj (0x10d8112b0). One of the two will be used. Which one is undefined.
(void *) $0 = 0x00006040001ad580
2018-03-06 09:39:58.536791+0800 MainProj[50214:3929509] AObject: 0x6040002033f0
2018-03-06 09:39:58.537293+0800 MainProj[50214:3929509] AObject: 0x6000000197e0

单例对象[AObject shareInstance]出现两个不同的内存地址。根据console提示信息,AObject类在LB0.framework和.app中都有实现。

4. 设置LC0为dynamic library,其他framework都是static library

objc[50340]: Class AObject is implemented in both /Users/hotacool/Library/Developer/Xcode/DerivedData/Test-fsrnousjgrbqxngqgmulsskbrcln/Build/Products/Debug-iphonesimulator/LC0.framework/LC0 (0x106ed5590) and /Users/hotacool/Library/Developer/CoreSimulator/Devices/E925C3DF-D46D-4DCF-9936-9239176C4189/data/Containers/Bundle/Application/8C4343C0-DF3F-442B-9CF2-3898EC678F39/MainProj.app/MainProj (0x106bef2b0). One of the two will be used. Which one is undefined.
objc[50340]: Class BObject is implemented in both /Users/hotacool/Library/Developer/Xcode/DerivedData/Test-fsrnousjgrbqxngqgmulsskbrcln/Build/Products/Debug-iphonesimulator/LC0.framework/LC0 (0x106ed55e0) and /Users/hotacool/Library/Developer/CoreSimulator/Devices/E925C3DF-D46D-4DCF-9936-9239176C4189/data/Containers/Bundle/Application/8C4343C0-DF3F-442B-9CF2-3898EC678F39/MainProj.app/MainProj (0x106bef300). One of the two will be used. Which one is undefined.
(void *) $0 = 0x00006000001bdf80
2018-03-06 09:47:11.090783+0800 MainProj[50340:3937289] AObject: 0x604000011dd0
2018-03-06 09:47:11.091422+0800 MainProj[50340:3937289] AObject: 0x604000011d70

同样出现单例对象多个地址问题。并且根据提示信息,AObject 、BObject都出现重复实现。

5. 所有framework都是dynamic library

2018-03-06 09:55:09.844002+0800 MainProj[50456:3944829] AObject: 0x604000015eb0

单例模式没有问题

推测结论

通过上述测试,只有当dynamic library依赖static library,上层同时添加dynamic library和static library到link binary library中时,会发生单例对象多个地址,如上面demo中static library中对象在.app和dynamic library多个实现的问题。

原因推测是:编译dynamic library时,会将依赖static library编译为二进制集成到dynamic library中,同时.app也会将static library编译到二进制中,导致有多个二进制实现,单例对象的调用地址会在编译时在二进制中写好,导致调用时有多个对象地址。

避免上述问题,一方面是梳理清楚依赖关系,如demo中,有很多多余的依赖,例如LC0的link binary libraries中只需加入LB0即可,无需LA0,通过删除多余的依赖,也可以保证不出现单例无效的问题。另一方面,在项目架构设计中,尽量避免dynamic和static相互依赖的情况,在物理层面上完全杜绝。

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

推荐阅读更多精彩内容

  • 静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别...
    吃瓜群众呀阅读 11,871评论 3 42
  • 仅以方便自己查阅记录前言1.静态库和动态库有什么异同?静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗...
    190CM阅读 4,170评论 0 4
  • 简介 cocoapods在1.4.0推出了static framework,先扒扒历史原因. dymanic fr...
    sea_biscute阅读 37,450评论 8 83
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 大概是几年前看了一部电影深受感触,写下“做自己,潇洒点”,成了我不变的QQ签名,这也是我一直向往着的生活态度。 置...
    向荣洵阅读 1,119评论 7 14