更新后变了h1z1黑夜模式式,求助

(jjjjjessy)
(队长别开枪)
(demondxx)
第三方登录:怎么恢复白天模式
曾在夜里点了夜间模式,现在变成黑屏,要求恢复白天原样_百度知道
怎么恢复白天模式
曾在夜里点了夜间模式,现在变成黑屏,要求恢复白天原样
怎么恢复白天模式
曾在夜里点了夜间模式,现在变成黑屏,要求恢复白天原样
我有更好的答案
这样很占内存很容易黑屏,关闭不必要的后台运行程序,让手机保持最佳状态操作不当导致黑屏:尽量不要同时打开很多软件:内存不足导致死机:经常清理内存手机黑屏死机解决方法。软件问题:根据你自己手机的内存或者配置下载适合自己的手机软件。如果手机配置低,要运行过大的软件这样很容易导致死机。其实现在的智能机跟电脑一样,在我们使用的时候要经常打理,这样运行才会畅快
采纳率:91%
来自团队:
请恢复白天模式!
1条折叠回答
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。自动更新了iPhone的微博,但是夜间模式没有了,有哪位大侠知道在哪里可以设定呢?还是新版微博没了夜间模式?
全部答案(共1个回答)
的菜单键,点夜间模式,然后下载夜间模式主题。如果满意,请好评
您好,您可以点击客户端首页底端显示的我-个性化-主题皮肤-我的主题页面查找即可。[可爱]
那就自己在下载个吧
按手机的菜单键,会出来
回复@大迪Ida:就是下拉菜单拉上来以后 airdrop 下面有个太阳和月亮的组合
答: 你好,有那种游戏的啊,偶尔可以给宝宝玩的,你也可以买专门的那种学习机的哦。
答: 这个手机发热是因cpu的原因。玩游戏、看电影时间久了自然都会热,这是现在只能手机的通病,暂时没有办法解决的, 苹果手机有的时候也很热。
答: 2002年5月份发起成立专注于智能手机、掌上电脑以及其它数码设备相关资讯的专业性网站,自成立起经过多次改版和调整,成为在国内享有很高知名度的专业智能手机和数码设...
答: 中关村在线可以
大家还关注
确定举报此问题
举报原因(必选):
广告或垃圾信息
激进时政或意识形态话题
不雅词句或人身攻击
侵犯他人隐私
其它违法和不良信息
报告,这不是个问题
报告原因(必选):
这不是个问题
这个问题分类似乎错了
这个不是我熟悉的地区
相关问答:123456789101112131415成熟的夜间模式解决方案2 years ago在当前主题为 DKThemeVersionNormal 时,将颜色保存至 normalBackgroundColor 中,然后再调用原 backgroundColor 的 setter 方法,更新视图的颜色。DKNightVersionManager这里只解决了颜色设置的问题,下面会说明,如果在主题改变时,实时更新颜色,而不用重新进入当前页面。整个 DKNightVersion 都是由一个 DKNightVersionManager 的单例来管理的,而它的主要工作就是负责改变应用的主题、并在主题改变时通知其它视图更新颜色:- (void)changeColor:(id &DKNightVersionChangeColorProtocol&)object {
if ([object respondsToSelector:@selector(changeColor)]) {
[object changeColor];
if ([object respondsToSelector:@selector(subviews)]) {
if (![object subviews]) {
// Basic case, do nothing.
for (id subview in [object subviews]) {
// recursive darken all the subviews of current view.
[self changeColor:subview];
if ([subview respondsToSelector:@selector(changeColor)]) {
[subview changeColor];
如果主题更新,那么就会递归地调用 changeColor 方法,刷新全部的视图颜色,而这个方法的实现比较简单:- (void)changeColor {
if ([DKNightVersionManager currentThemeVersion] == DKThemeVersionNormal) {
self.backgroundColor = self.normalBackgroundC
self.backgroundColor = self.nightBackgroundC
上面就是整个框架在 1.0 版本时的实现思路。不过这个版本的 DKNightVersion 在实际应用中会有比较多的问题:在高速滚动的 scrollView 上面来回切换夜间模式,会出现颜色错乱的问题由于对 backgroundColor 属性进行不合适的方法调剂,其行为无法预测,比如:在设置颜色后,再取出,不一定与设置时传入的颜色相同无法适配第三方 UI 控件使用色表的版本为了解决 1.0 中的各种问题,我决定在 2.0 版本中放弃对 nightBackgroundColor 的使用,并且重新设计底层的实现,转而使用更为稳定、安全的方法实现夜间模式,先看一下效果图:新的实现不仅能够支持夜间模式,而且能够支持多主题。DKColorPicker与上一个版本实现上的不同,在 2.0 中删除了全部的 nightBackgroundColor,使用一个名为 dk_backgroundColorPicker 的属性取代它。@property (nonatomic, copy) DKColorPicker dk_backgroundColorPicker;
这个属性其实就是一个 block,它接收参数 DKThemeVersion *themeVersion,但是会返回一个 UIColor *:在第一次传入 picker 或者每次主题改变时,都会将当前主题 DKThemeVersion 传入 picker 并执行,然后,将得到的 UIColor 赋值给对应的属性 backgroundColor 更新视图颜色。typedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion);
比如下面使用 DKColorPickerWithRGB 创建一个临时的 DKColorPicker:在 DKThemeVersionNormal 时返回 0xffffff在 DKThemeVersionNight 时返回 0x343434在自定义的主题下返回 0xfafafa (这里的顺序与色表中主题的顺序有关)cell.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0xxfafafa);
同时,每一个对象还持有一个 pickers 数组,来存储自己的全部 DKColorPicker:@interface NSObject ()
@property (nonatomic, strong) NSMutableDictionary&NSString *, DKColorPicker& *pickers;
在第一次使用这个属性时,当前对象注册为 DKNightVersionThemeChangingNotificaiton 通知的观察者。在每次收到通知时,都会调用 night_update 方法,将当前主题传入 DKColorPicker,并再次执行,并将结果传入对应的属性 [self performSelector:sel withObject:result]。- (void)night_updateColor {
[self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker
_Nonnull picker, BOOL * _Nonnull stop) {
SEL sel = NSSelectorFromString(selector);
id result = picker(self.dk_manager.themeVersion);
[UIView animateWithDuration:DKNightVersionAnimationDuration
animations:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:sel withObject:result];
#pragma clang diagnostic pop
也就是说,在每次改变主题的时候,都会发出通知。DKColorTable虽然我们在上面临时创建了一些 DKColorPicker。不过在 DKNightVersion 中,我更推荐使用色表,来减少相同的 DKColorPicker 的创建,并且能够更好地管理整个应用中的颜色:NORMAL
#fafafa BG
#aaaaaa SEP
#fa0000 TINT
#000000 TEXT
#ffffff BAR
上面就是默认色表文件 DKColorTable.txt 中的内容,其中,第一行表示主题,NORMAL 主题必须存在,而且必须为第一列,而最右面的 BG、SEP 就是对应 DKColorPicker 的 key。self.tableView.dk_backgroundColorPicker =
DKColorPickerWithKey(BG);
在使用时,上面的代码就相当于返回了一个在 NORMAL 时返回 #ffffff、NIGHT 时返回 #343434 以及 RED 时返回 #fafafa 的 DKColorPicker。pickerify虽然说,我们使用色表以及 DKColorPicker 解决了,但是,到目前为止我们还没有解决第三方框架的问题。比如我们使用了某个第三方框架,或者自己添加了某个 color 属性,比如说:@interface DKView ()
@property (nonatomic, strong) UIColor *weirdC
weirdColor 并没有对应的 DKColorPicker,但是,我们可以通过 pickerify 在想要使用 dk_weirdColorPicker 的地方生成这个对应的 picker:@pickerify(DKView, weirdColor);
然后,我们就可以使用 dk_weirdColorPicker 属性了:view.dk_weirdColorPicker = DKColorPickerWithKey(BG);
pickerify 其实是一个宏:#define pickerify(KLASS, PROPERTY) interface \
KLASS (Night) \
@property (nonatomic, copy, setter = dk_set ## PROPERTY ## Picker:) DKColorPicker dk_ ## PROPERTY ## P \
@interface \
KLASS () \
@property (nonatomic, strong) NSMutableDictionary&NSString *, DKColorPicker& * \
@implementation \
KLASS (Night) \
- (DKColorPicker)dk_ ## PROPERTY ## Picker { \
return objc_getAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker)); \
- (void)dk_set ## PROPERTY ## Picker:(DKColorPicker)picker { \
objc_setAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); \
[self setValue:picker(self.dk_manager.themeVersion) forKeyPath:@keypath(self, PROPERTY)];\
[self.pickers setValue:[picker copy] forKey:_DKSetterWithPROPERTYerty(@#PROPERTY)]; \
这个宏根据传入的类和属性名,为我们生成了对应 picker 的存取方法,它也可以说是一种元编程的手段。这里生成的 setter 方法不是标准意义上的驼峰命名法 dk_setweirdColorPicker:,因为我不知道怎么才能让大写首字母之后的属性添加到这里(如果各位读者有解决方案,欢迎提 PR 或者 issue)。嵌入式 Ruby由于框架中很多的代码,都是重复的,所以在这里使用了嵌入式 Ruby 模板来生成对应的文件 color.m.irb://
&%= klass.name %&+Night.m
&%= klass.name %&+Night
Copyright (c) 2015 Draveness. All rights reserved.
These files are generated by ruby script, if you want to modify code
in this file, you are supposed to update the ruby code, run it and
test it. And finally open a pull request.
#import "&%= klass.name %&+Night.h"
#import "DKNightVersionManager.h"
#import &objc/runtime.h&
@interface &%= klass.name %& ()
@property (nonatomic, strong) NSMutableDictionary&NSString *, DKColorPicker& *
@implementation &%= klass.name %& (Night)
&% klass.properties.each do |property| %&&%= """
- (DKColorPicker)dk_#{property.name}Picker {
return objc_getAssociatedObject(self, @selector(dk_#{property.name}Picker));
- (void)dk_set#{property.cap_name}Picker:(DKColorPicker)picker {
objc_setAssociatedObject(self, @selector(dk_#{property.name}Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC);
self.#{property.name} = picker(self.dk_manager.themeVersion);
[self.pickers setValue:[picker copy] forKey:@\"#{property.setter}\"];
""" %&&% end %&
这部分的实现并不在这篇文章的讨论范围之内,如果,对这部分看兴趣,可以看一下仓库中的 generator 文件夹,其中包含了代码生成器的全部代码。小结如果你对 DKNightVersion 的使用有兴趣,可以查看仓库的
文件,有人会说不要在项目中 ObjC runtime,我个人觉得是没有问题,AFNetworking、 BlocksKit 也使用方法调剂来改变原有方法的实现,不能因为它强大就不使用它;正相反,有时候,使用 runtime 才能优雅地解决问题。关注仓库,及时获得更新:Follow:
· GitHub)6收藏分享举报文章被以下专栏收录{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&isPending&:false,&contributes&:[{&sourceColumn&:{&lastUpdated&:,&description&:&关于 iOS 开发的、可能会夹带私货的干货&,&permission&:&COLUMN_PUBLIC&,&memberId&:4603437,&contributePermission&:&COLUMN_PUBLIC&,&translatedCommentPermission&:&all&,&canManage&:true,&intro&:&&,&urlToken&:&iOS-Source-Code&,&id&:11553,&imagePath&:&38b13aa90f9b5e8ecb446c52eabcaed1.jpeg&,&slug&:&iOS-Source-Code&,&applyReason&:&0&,&name&:&iOS 源代码解析&,&title&:&iOS 源代码解析&,&url&:&https:\u002F\u002Fzhuanlan.zhihu.com\u002FiOS-Source-Code&,&commentPermission&:&COLUMN_ALL_CAN_COMMENT&,&canPost&:true,&created&:,&state&:&COLUMN_NORMAL&,&followers&:1536,&avatar&:{&id&:&38b13aa90f9b5e8ecb446c52eabcaed1&,&template&:&https:\u002F\u002Fpic4.zhimg.com\u002F{id}_{size}.jpg&},&activateAuthorRequested&:false,&following&:false,&imageUrl&:&https:\u002F\u002Fpic4.zhimg.com\u002F38b13aa90f9b5e8ecb446c52eabcaed1_l.jpg&,&articlesCount&:22},&state&:&accepted&,&targetPost&:{&titleImage&:&https:\u002F\u002Fpic2.zhimg.com\u002F0ef8e7a07d069c57d5b59_r.jpg&,&lastUpdated&:,&imagePath&:&0ef8e7a07d069c57d5b59.jpg&,&permission&:&ARTICLE_PUBLIC&,&topics&:[77,],&summary&:&关注仓库,及时获得更新:\u003Ca href=\&GitHub%20-%20Draveness\u002FiOS-Source-Code-Analyze:%20%E5%85%B3%E4%BA%8E%20iOS%20%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90%E7%9A%84%E6%96%87%E7%AB%A0\&\u003EiOS-Source-Code-Analyze\u003C\u002Fa\u003E Follow: \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002FDraveness%2520%28Draveness\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EDraveness · Github\u003C\u002Fa\u003E从开始写 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EDKNightVersion\u003C\u002Fa\u003E 这个框架到现在已经将近一年了,目前整个框架的设计也趋于稳定。其实夜间模式的实现就是相当于\u003Cstrong\u003E多主题加颜色管理\u003C\u002Fstrong\u003E。而最新版本的 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EDKNightVersion\u003C\u002Fa\u003E 已经…&,&copyPermission&:&ARTICLE_COPYABLE&,&translatedCommentPermission&:&all&,&likes&:0,&origAuthorId&:0,&publishedTime&:&T15:30:03+08:00&,&sourceUrl&:&&,&urlToken&:,&id&:519999,&withContent&:false,&slug&:,&bigTitleImage&:false,&title&:&成熟的夜间模式解决方案&,&url&:&\u002Fp\u002F&,&commentPermission&:&ARTICLE_ALL_CAN_COMMENT&,&snapshotUrl&:&&,&created&:,&comments&:0,&columnId&:11553,&content&:&&,&parentId&:0,&state&:&ARTICLE_PUBLISHED&,&imageUrl&:&https:\u002F\u002Fpic2.zhimg.com\u002F0ef8e7a07d069c57d5b59_r.jpg&,&author&:{&bio&:&疯狂造轮子的工程师&,&isFollowing&:false,&hash&:&eabb6a6afb88bb4c0fe6e351&,&uid&:48,&isOrg&:false,&slug&:&draveness&,&isFollowed&:false,&description&:&https:\u002F\u002Fgithub.com\u002FDraveness&,&name&:&Draveness&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fdraveness&,&avatar&:{&id&:&d29a6e13d3898ccdfa23&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&memberId&:4603437,&excerptTitle&:&&,&voteType&:&ARTICLE_VOTE_CLEAR&},&id&:356934}],&title&:&成熟的夜间模式解决方案&,&author&:&draveness&,&content&:&\u003Cblockquote\u003E\u003Cp\u003E关注仓库,及时获得更新:\u003Ca href=\&GitHub%20-%20Draveness\u002FiOS-Source-Code-Analyze:%20%E5%85%B3%E4%BA%8E%20iOS%20%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90%E7%9A%84%E6%96%87%E7%AB%A0\&\u003EiOS-Source-Code-Analyze\u003C\u002Fa\u003E\u003Cbr\u003EFollow: \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002FDraveness%2520%28Draveness\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EDraveness · Github\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E从开始写 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EDKNightVersion\u003C\u002Fa\u003E 这个框架到现在已经将近一年了,目前整个框架的设计也趋于稳定。\u003C\u002Fp\u003E\u003Cp\u003E其实夜间模式的实现就是相当于\u003Cstrong\u003E多主题加颜色管理\u003C\u002Fstrong\u003E。而最新版本的 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EDKNightVersion\u003C\u002Fa\u003E 已经很好的解决了这个问题。\u003C\u002Fp\u003E\u003Cp\u003E在正式介绍目前版本的实现之前,我会先简单介绍一下 1.0 时代的 DKNightVersion 的实现,为各位读者带来一些新的思路,也确实想梳理一下这个框架是如何演变的。\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E我们会以对 backgroundColor 为例说明整个框架的工作原理。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Ch2\u003E方法调剂的版本\u003C\u002Fh2\u003E\u003Cp\u003E如何在不改变原有的架构,甚至不改变原有的代码的基础上,为应用优雅地添加夜间模式成为很多开发者不得不面对的问题。这也是 1.0 时代的 DKNightVersion 想要实现的目标。\u003C\u002Fp\u003E\u003Cp\u003E其核心思路就是\u003Cstrong\u003E使用方法调剂修改 backgroundColor 的存取方法\u003C\u002Fstrong\u003E。\u003C\u002Fp\u003E\u003Ch3\u003E使用 nightBackgroundColor\u003C\u002Fh3\u003E\u003Cp\u003E在思考之后,我想到,想要在不改动原有代码的基础上实现夜间模式只能通过在\u003Cstrong\u003E分类\u003C\u002Fstrong\u003E中添加 nightBackgroundColor 属性,并且使用方法调剂改变 backgroundColor 的 setter 方法。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E- (void)hook_setBackgroundColor:(UIColor*)backgroundColor {\n
if ([DKNightVersionManager currentThemeVersion] == DKThemeVersionNormal) {\n
[self setNormalBackgroundColor:backgroundColor];\n
[self hook_setBackgroundColor:backgroundColor];\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E在当前主题为 DKThemeVersionNormal 时,将颜色保存至 normalBackgroundColor 中,然后再调用原 backgroundColor 的 setter 方法,更新视图的颜色。\u003C\u002Fp\u003E\u003Ch3\u003EDKNightVersionManager\u003C\u002Fh3\u003E\u003Cp\u003E这里只解决了颜色设置的问题,下面会说明,如果在主题改变时,实时更新颜色,而不用重新进入当前页面。\u003C\u002Fp\u003E\u003Cp\u003E整个 DKNightVersion 都是由一个 DKNightVersionManager 的单例来管理的,而它的主要工作就是负责\u003Cstrong\u003E改变应用的主题\u003C\u002Fstrong\u003E、并在主题改变时\u003Cstrong\u003E通知其它视图更新颜色\u003C\u002Fstrong\u003E:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E- (void)changeColor:(id &DKNightVersionChangeColorProtocol&)object {\n
if ([object respondsToSelector:@selector(changeColor)]) {\n
[object changeColor];\n
if ([object respondsToSelector:@selector(subviews)]) {\n
if (![object subviews]) {\n
\u002F\u002F Basic case, do nothing.\\n
} else {\n
for (id subview in [object subviews]) {\n
\u002F\u002F recursive darken all the subviews of current view.\n
[self changeColor:subview];\n
if ([subview respondsToSelector:@selector(changeColor)]) {\n
[subview changeColor];\n
}\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E如果主题更新,那么就会递归地调用 changeColor 方法,刷新全部的视图颜色,而这个方法的实现比较简单:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E- (void)changeColor {\n
if ([DKNightVersionManager currentThemeVersion] == DKThemeVersionNormal) {\n
self.backgroundColor = self.normalBackgroundC\n
} else {\n
self.backgroundColor = self.nightBackgroundC\n
}\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E上面就是整个框架在 1.0 版本时的实现思路。不过这个版本的 DKNightVersion 在实际应用中会有比较多的问题:\u003C\u002Fp\u003E\u003Col\u003E\u003Cli\u003E在高速滚动的 scrollView 上面来回切换夜间模式,会出现颜色错乱的问题\u003C\u002Fli\u003E\u003Cli\u003E由于对 backgroundColor 属性进行\u003Cstrong\u003E不合适的\u003C\u002Fstrong\u003E方法调剂,其行为无法预测,比如:在设置颜色后,再取出,不一定与设置时传入的颜色相同\u003C\u002Fli\u003E\u003Cli\u003E无法适配第三方 UI 控件\u003C\u002Fli\u003E\u003C\u002Fol\u003E\u003Ch2\u003E使用色表的版本\u003C\u002Fh2\u003E\u003Cp\u003E为了解决 1.0 中的各种问题,我决定在 2.0 版本中放弃对 nightBackgroundColor 的使用,并且重新设计底层的实现,转而使用更为\u003Cstrong\u003E稳定\u003C\u002Fstrong\u003E、\u003Cstrong\u003E安全\u003C\u002Fstrong\u003E的方法实现夜间模式,先看一下效果图:\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E新的实现不仅能够支持夜间模式,而且能够支持多主题。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Ch3\u003EDKColorPicker\u003C\u002Fh3\u003E\u003Cp\u003E与上一个版本实现上的不同,在 2.0 中删除了全部的 nightBackgroundColor,使用一个名为 dk_backgroundColorPicker 的属性取代它。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003E@property\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Enonatomic\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Ecopy\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EDKColorPicker\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edk_backgroundColorPicker\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这个属性其实就是一个 block,它接收参数 DKThemeVersion *themeVersion,但是会返回一个 UIColor *:\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E在第一次传入 picker 或者每次主题改变时,都会将当前主题 DKThemeVersion 传入 picker 并执行,然后,将得到的 UIColor 赋值给对应的属性 backgroundColor 更新视图颜色。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Etypedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E比如下面使用 DKColorPickerWithRGB 创建一个临时的 DKColorPicker:\u003C\u002Fp\u003E\u003Col\u003E\u003Cli\u003E在 DKThemeVersionNormal 时返回 0xffffff\u003C\u002Fli\u003E\u003Cli\u003E在 DKThemeVersionNight 时返回 0x3C\u002Fli\u003E\u003Cli\u003E在自定义的主题下返回 0xfafafa (这里的顺序与色表中主题的顺序有关)\u003C\u002Fli\u003E\u003C\u002Fol\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003Ecell.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0xxfafafa);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E同时,每一个对象还持有一个 pickers 数组,来存储自己的全部 DKColorPicker:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003E@interface\u003C\u002Fspan\u003E \u003Cspan class=\&bp\&\u003ENSObject\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E()\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&k\&\u003E@property\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Enonatomic\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Estrong\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&bp\&\u003ENSMutableDictionary\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E\u003Cspan class=\&bp\&\u003ENSString\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EDKColorPicker\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Epickers\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&k\&\u003E@end\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E在第一次使用这个属性时,当前对象注册为 DKNightVersionThemeChangingNotificaiton 通知的观察者。\u003C\u002Fp\u003E\u003Cp\u003E在每次收到通知时,都会调用 night_update 方法,将当前主题传入 DKColorPicker,并再次执行,并将结果传入对应的属性 [self performSelector:sel withObject:result]。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E- (void)night_updateColor {\n
[self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker
_Nonnull picker, BOOL * _Nonnull stop) {\n
SEL sel = NSSelectorFromString(selector);\n
id result = picker(self.dk_manager.themeVersion);\n
[UIView animateWithDuration:DKNightVersionAnimationDuration\n
animations:^{\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \&-Warc-performSelector-leaks\&\n
[self performSelector:sel withObject:result];\n#pragma clang diagnostic pop\n
}];\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E也就是说,在每次改变主题的时候,都会发出通知。\u003C\u002Fp\u003E\u003Ch3\u003EDKColorTable\u003C\u002Fh3\u003E\u003Cp\u003E虽然我们在上面临时创建了一些 DKColorPicker。不过在 DKNightVersion 中,我更推荐使用色表,来减少相同的 DKColorPicker 的创建,并且能够更好地管理整个应用中的颜色:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003ENORMAL
RED\n#ffffff
#fafafa BG\n#aaaaaa
#aaaaaa SEP\n#0000ff
#fa0000 TINT\n#000000
#000000 TEXT\n#ffffff
#ffffff BAR\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E上面就是默认色表文件 DKColorTable.txt 中的内容,其中,第一行表示主题,NORMAL 主题必须存在,而且必须为第一列,而最右面的 BG、SEP 就是对应 DKColorPicker 的 key。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&nb\&\u003Eself\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EtableView\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Edk_backgroundColorPicker\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E
\u003Cspan class=\&n\&\u003EDKColorPickerWithKey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EBG\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E在使用时,上面的代码就相当于返回了一个在 NORMAL 时返回 #ffffff、NIGHT 时返回 #343434 以及 RED 时返回 #fafafa 的 DKColorPicker。\u003C\u002Fp\u003E\u003Ch3\u003Epickerify\u003C\u002Fh3\u003E\u003Cp\u003E虽然说,我们使用色表以及 DKColorPicker 解决了,但是,到目前为止我们还没有解决第三方框架的问题。\u003C\u002Fp\u003E\u003Cp\u003E比如我们使用了某个第三方框架,或者自己添加了某个 color 属性,比如说:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E@interface DKView ()\n\n@property (nonatomic, strong) UIColor *weirdC\n\n@end\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003EweirdColor 并没有对应的 DKColorPicker,但是,我们可以通过 pickerify 在想要使用 dk_weirdColorPicker 的地方生成这个对应的 picker:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E@\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Epickerify\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EDKView\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EweirdColor\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E然后,我们就可以使用 dk_weirdColorPicker 属性了:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Eview\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Edk_weirdColorPicker\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EDKColorPickerWithKey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EBG\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003Epickerify 其实是一个宏:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&cp\&\u003E#define pickerify(KLASS, PROPERTY) interface \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
KLASS (Night) \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@property (nonatomic, copy, setter = dk_set ## PROPERTY ## Picker:) DKColorPicker dk_ ## PROPERTY ## P \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@end \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@interface \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
KLASS () \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@property (nonatomic, strong) NSMutableDictionary&NSString *, DKColorPicker& * \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@end \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@implementation \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
KLASS (Night) \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
- (DKColorPicker)dk_ ## PROPERTY ## Picker { \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
return objc_getAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker)); \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
} \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
- (void)dk_set ## PROPERTY ## Picker:(DKColorPicker)picker { \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
objc_setAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
[self setValue:picker(self.dk_manager.themeVersion) forKeyPath:@keypath(self, PROPERTY)];\\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
[self.pickers setValue:[picker copy] forKey:_DKSetterWithPROPERTYerty(@#PROPERTY)]; \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
} \\\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E
@end\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这个宏根据传入的类和属性名,为我们生成了对应 picker 的存取方法,它也可以说是一种元编程的手段。\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E这里生成的 setter 方法不是标准意义上的驼峰命名法 dk_setweirdColorPicker:,因为我不知道怎么才能让大写首字母之后的属性添加到这里(如果各位读者有解决方案,欢迎提 PR 或者 issue)。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Ch2\u003E嵌入式 Ruby\u003C\u002Fh2\u003E\u003Cp\u003E由于框架中很多的代码,都是重复的,所以在这里使用了\u003Cstrong\u003E嵌入式 Ruby 模板\u003C\u002Fstrong\u003E来生成对应的文件 color.m.irb:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u002F\u002F\n\u002F\u002F
&%= klass.name %&+Night.m\n\u002F\u002F
&%= klass.name %&+Night\n\u002F\u002F\n\u002F\u002F
Copyright (c) 2015 Draveness. All rights reserved.\n\u002F\u002F\n\u002F\u002F
These files are generated by ruby script, if you want to modify code\n\u002F\u002F
in this file, you are supposed to update the ruby code, run it and\n\u002F\u002F
test it. And finally open a pull request.\n\n#import \&&%= klass.name %&+Night.h\&\n#import \&DKNightVersionManager.h\&\n#import &objc\u002Fruntime.h&\n\n@interface &%= klass.name %& ()\n\n@property (nonatomic, strong) NSMutableDictionary&NSString *, DKColorPicker& *\n\n@end\n\n@implementation &%= klass.name %& (Night)\n\n&% klass.properties.each do |property| %&&%= \&\&\&\n- (DKColorPicker)dk_#{property.name}Picker {\n
return objc_getAssociatedObject(self, @selector(dk_#{property.name}Picker));\n}\n\n- (void)dk_set#{property.cap_name}Picker:(DKColorPicker)picker {\n
objc_setAssociatedObject(self, @selector(dk_#{property.name}Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC);\n
self.#{property.name} = picker(self.dk_manager.themeVersion);\n
[self.pickers setValue:[picker copy] forKey:@\\\&#{property.setter}\\\&];\n}\n\&\&\& %&&% end %&\n\n@end\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这部分的实现并不在这篇文章的讨论范围之内,如果,对这部分看兴趣,可以看一下仓库中的 generator 文件夹,其中包含了代码生成器的全部代码。\u003C\u002Fp\u003E\u003Ch2\u003E小结\u003C\u002Fh2\u003E\u003Cp\u003E如果你对 DKNightVersion 的使用有兴趣,可以查看仓库的 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EREADME\u003C\u002Fa\u003E 文件,有人会说不要在项目中 ObjC runtime,我个人觉得是没有问题,AFNetworking、 BlocksKit 也使用方法调剂来改变原有方法的实现,不能因为它强大就不使用它;正相反,有时候,使用 runtime 才能优雅地解决问题。\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E关注仓库,及时获得更新:\u003Ca href=\&GitHub%20-%20Draveness\u002FiOS-Source-Code-Analyze:%20%E5%85%B3%E4%BA%8E%20iOS%20%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90%E7%9A%84%E6%96%87%E7%AB%A0\&\u003EiOS-Source-Code-Analyze\u003C\u002Fa\u003E\u003Cbr\u003EFollow: \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002FDraveness%2520%28Draveness\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EDraveness · Github\u003C\u002Fa\u003E · GitHub)\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E&,&updated&:new Date(&T07:30:03.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:11,&collapsedCount&:0,&likeCount&:6,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic2.zhimg.com\u002F0ef8e7a07d069c57d5b59_r.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&reviewers&:[],&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&iOS&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Objective-C&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&夜间模式&}],&adminClosedComment&:false,&titleImageSize&:{&width&:1024,&height&:513},&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&column&:{&slug&:&iOS-Source-Code&,&name&:&iOS 源代码解析&},&tipjarState&:&inactivated&,&annotationAction&:[],&sourceUrl&:&&,&pageCommentsCount&:11,&hasPublishingDraft&:false,&snapshotUrl&:&&,&publishedTime&:&T15:30:03+08:00&,&url&:&\u002Fp\u002F&,&lastestLikers&:[{&bio&:&iOS Dev&,&isFollowing&:false,&hash&:&68f051f3fef4f8a82cfae&,&uid&:76,&isOrg&:false,&slug&:&ming-duan&,&isFollowed&:false,&description&:&对 iOS 略有涉猎的程序猿&,&name&:&GeekDuan&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fming-duan&,&avatar&:{&id&:&f4c18df3ddb036f3c78d&,&template&:&https:\u002F\u002Fpic2.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&骑行、篮球爱好者,历史皮毛,不悔初心的人体修理工在读&,&isFollowing&:false,&hash&:&efdb307f37ca2da5d2571babc76db26c&,&uid&:835800,&isOrg&:false,&slug&:&zhao-shuai-9-2&,&isFollowed&:false,&description&:&随心走,不失心&,&name&:&吃饭睡觉爱阳阳&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fzhao-shuai-9-2&,&avatar&:{&id&:&v2-0d32a86da39fd1a4a6df4bd848a1ddb3&,&template&:&https:\u002F\u002Fpic1.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&一路狂奔&,&isFollowing&:false,&hash&:&c7ffe0b5cbdd1e5d267999ea&,&uid&:52,&isOrg&:false,&slug&:&LittleDunDun&,&isFollowed&:false,&description&:&知乎上逛一圈。。。&,&name&:&贺晨&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002FLittleDunDun&,&avatar&:{&id&:&b2c2db7ba357e581d5a53d4eaeffb87f&,&template&:&https:\u002F\u002Fpic1.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&码字者..(仅限26个字母)&,&isFollowing&:false,&hash&:&b1e60ca3d86fc70c36dee2fef158aec1&,&uid&:60,&isOrg&:false,&slug&:&kkkkkkkkkkkkkkkkkkkk&,&isFollowed&:false,&description&:&码字者..(仅限26个字母)&,&name&:&biubiu&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fkkkkkkkkkkkkkkkkkkkk&,&avatar&:{&id&:&1bbd480c9bae4bf88baef&,&template&:&https:\u002F\u002Fpic1.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&~~&,&isFollowing&:false,&hash&:&47c51de21a39e29e697ad&,&uid&:00,&isOrg&:false,&slug&:&dhcdht&,&isFollowed&:false,&description&:&A&,&name&:&董宏&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fdhcdht&,&avatar&:{&id&:&b1c7d3ad6d0a71be0207eb&,&template&:&https:\u002F\u002Fpic4.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false}],&summary&:&关注仓库,及时获得更新:\u003Ca href=\&GitHub%20-%20Draveness\u002FiOS-Source-Code-Analyze:%20%E5%85%B3%E4%BA%8E%20iOS%20%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90%E7%9A%84%E6%96%87%E7%AB%A0\&\u003EiOS-Source-Code-Analyze\u003C\u002Fa\u003E Follow: \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002FDraveness%2520%28Draveness\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EDraveness · Github\u003C\u002Fa\u003E从开始写 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EDKNightVersion\u003C\u002Fa\u003E 这个框架到现在已经将近一年了,目前整个框架的设计也趋于稳定。其实夜间模式的实现就是相当于\u003Cstrong\u003E多主题加颜色管理\u003C\u002Fstrong\u003E。而最新版本的 \u003Ca href=\&GitHub%20-%20Draveness\u002FDKNightVersion:%20Manage%20Colors,%20Integrate%20Night\u002FMultiple%20Themes\&\u003EDKNightVersion\u003C\u002Fa\u003E 已经…&,&reviewingCommentsCount&:0,&meta&:{&previous&:{&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic1.zhimg.com\u002F50\u002F653bb4fbafa03e5be685dcb_xl.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&iOS&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&iOS 开发&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Objective-C&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&疯狂造轮子的工程师&,&isFollowing&:false,&hash&:&eabb6a6afb88bb4c0fe6e351&,&uid&:48,&isOrg&:false,&slug&:&draveness&,&isFollowed&:false,&description&:&https:\u002F\u002Fgithub.com\u002FDraveness&,&name&:&Draveness&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fdraveness&,&avatar&:{&id&:&d29a6e13d3898ccdfa23&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&column&:{&slug&:&iOS-Source-Code&,&name&:&iOS 源代码解析&},&content&:&\u003Cblockquote\u003E\u003Cp\u003E关注仓库,及时获得更新:\u003Ca href=\&GitHub%20-%20Draveness\u002FiOS-Source-Code-Analyze:%20%E5%85%B3%E4%BA%8E%20iOS%20%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90%E7%9A%84%E6%96%87%E7%AB%A0\& class=\&\&\u003EiOS-Source-Code-Analyze\u003C\u002Fa\u003E\u003Cbr\u003EGithub: \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002FDraveness%2520%28Draveness\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E@Draveness\u003C\u002Fa\u003E · GitHub)\u003C\u002Fp\u003E\u003Cp\u003E因为 ObjC 的 runtime 只能在 Mac OS 下才能编译,所以文章中的代码都是在 Mac OS,也就是 x86_64 架构下运行的,对于在 arm64 中运行的代码会特别说明。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Ch2\u003E写在前面\u003C\u002Fh2\u003E\u003Cblockquote\u003E\u003Cp\u003E文章的标题与其说是问各位读者,不如说是问笔者自己:\u003Cstrong\u003E我\u003C\u002Fstrong\u003E真的了解 + load 方法么?\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E+ load 作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个\u003Cstrong\u003E在整个文件被加载到运行时,在 main 函数调用之前被 ObjC 运行时调用的钩子方法\u003C\u002Fstrong\u003E。其中关键字有这么几个:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E文件刚加载\u003C\u002Fli\u003E\u003Cli\u003Emain 函数之前\u003C\u002Fli\u003E\u003Cli\u003E钩子方法\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E我在阅读 ObjC 源代码之前,曾经一度感觉自己对 + load 方法的作用非常了解,直到看了源代码中的实现,才知道以前的以为,只是自己的以为罢了。\u003C\u002Fp\u003E\u003Cp\u003E这篇文章会假设你知道:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E使用过 + load 方法\u003C\u002Fli\u003E\u003Cli\u003E知道 + load 方法的调用顺序(文章中会简单介绍)\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E在这篇文章中并不会用大篇幅介绍 + load 方法的作用其实也没几个作用,关注点主要在以下两个问题上:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E+ load 方法是如何被调用的\u003C\u002Fli\u003E\u003Cli\u003E+ load 方法为什么会有这种调用顺序\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Ch2\u003Eload 方法的调用栈\u003C\u002Fh2\u003E\u003Cp\u003E首先来通过 load 方法的调用栈,分析一下它到底是如何被调用的。\u003C\u002Fp\u003E\u003Cp\u003E下面是程序的全部代码:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&c1\&\u003E\u002F\u002F main.m\u003C\u002Fspan\u003E\n\u003Cspan class=\&cp\&\u003E#import &Foundation\u002FFoundation.h&\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&k\&\u003E@interface\u003C\u002Fspan\u003E \u003Cspan class=\&nc\&\u003EXXObject\u003C\u002Fspan\u003E : \u003Cspan class=\&bp\&\u003ENSObject\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003E@end\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&k\&\u003E@implementation\u003C\u002Fspan\u003E \u003Cspan class=\&nc\&\u003EXXObject\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&p\&\u003E+\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&kt\&\u003Evoid\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\u003Cspan class=\&nf\&\u003Eload\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003ENSLog\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s\&\u003E@\&XXObject load\&\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&k\&\u003E@end\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&kt\&\u003Eint\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Emain\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&kt\&\u003Eint\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Eargc\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&kt\&\u003Echar\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Eargv\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[])\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003E@autoreleasepool\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E代码总共只实现了一个 XXObject 的 + load 方法,主函数中也没有任何的东西:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&http:\u002F\u002Fpic2.zhimg.com\u002F3d3f69f6041b04fcde745e5c9492946d_b.jpg\& data-rawwidth=\&733\& data-rawheight=\&493\& class=\&origin_image zh-lightbox-thumb\& width=\&733\& data-original=\&http:\u002F\u002Fpic2.zhimg.com\u002F3d3f69f6041b04fcde745e5c9492946d_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cbr\u003E\u003Cp\u003E虽然在主函数中什么方法都没有调用,但是运行之后,依然打印了 XXObject load 字符串,也就是说调用了 + load 方法。\u003C\u002Fp\u003E\u003Ch3\u003E使用符号断点\u003C\u002Fh3\u003E\u003Cp\u003E使用 Xcode 添加一个符号断点 +[XXObject load]:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&http:\u002F\u002Fpic3.zhimg.com\u002Fc1cecaa109bbcd69d5a54a_b.jpg\& data-rawwidth=\&476\& data-rawheight=\&191\& class=\&origin_image zh-lightbox-thumb\& width=\&476\& data-original=\&http:\u002F\u002Fpic3.zhimg.com\u002Fc1cecaa109bbcd69d5a54a_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cbr\u003E\u003Cblockquote\u003E\u003Cp\u003E注意这里 + 和 [ 之间没有空格\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cp\u003E为什么要加一个符号断点呢?因为这样看起来比较高级。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E重新运行程序。这时,代码会停在 NSLog(@\&XXObject load\&); 这一行的实现上:\u003C\u002Fp\u003E\u003Cp\u003E左侧的调用栈很清楚的告诉我们,哪些方法被调用了:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&http:\u002F\u002Fpic2.zhimg.com\u002Fb73afb0b88fd1a4826fd_b.jpg\& data-rawwidth=\&1201\& data-rawheight=\&808\& class=\&origin_image zh-lightbox-thumb\& width=\&1201\& data-original=\&http:\u002F\u002Fpic2.zhimg.com\u002Fb73afb0b88fd1a4826fd_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E
\u003Cspan class=\&o\&\u003E+\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EXXObject\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Eload\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E]\u003C\u002Fspan\u003E\n\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E
\u003Cspan class=\&n\&\u003Ecall_class_loads\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E()\u003C\u002Fspan\u003E\n\u003Cspan class=\&mi\&\u003E2\u003C\u002Fspan\u003E
\u003Cspan class=\&n\&\u003Ecall_load_methods\u003C\u002Fspan\u003E\n\u003Cspan class=\&mi\&\u003E3\u003C\u002Fspan\u003E
\u003Cspan class=\&n\&\u003Eload_images\u003C\u002Fspan\u003E\n\u003Cspan class=\&mi\&\u003E4\u003C\u002Fspan\u003E
\u003Cspan class=\&n\&\u003Edyld\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E::\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EnotifySingle\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Edyld_image_states\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EImageLoader\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003Cspan class=\&mi\&\u003E11\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003E_dyld_start\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cblockquote\u003E\u003Cp\u003E\u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Fios\u002Fdocumentation\u002FSystem\u002FConceptual\u002FManPages_iPhoneOS\u002Fman3\u002Fdyld.3.html\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003Edyld\u003C\u002Fa\u003E 是 the dynamic link editor 的缩写,它是苹果的\u003Cem\u003E动态链接器\u003C\u002Fem\u003E。\u003C\u002Fp\u003E\u003Cp\u003E在系统内核做好程序准备工作之后,交由 dyld 负责余下的工作。本文不会对其进行解释\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E每当有新的镜像加载之后,都会执行 3 load_images 方法进行回调,这里的回调是在整个运行时初始化时 _objc_init 注册的(会在之后的文章中具体介绍):\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Edyld_register_image_state_change_handler\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Edyld_image_state_dependents_initialized\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&cm\&\u003E\u002F*not batch*\u002F\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Eload_images\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E有新的镜像被加载到 runtime 时,调用 load_images 方法,并传入最新镜像的信息列表 infoList:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&kt\&\u003Echar\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\n\u003Cspan class=\&nf\&\u003Eload_images\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Eenum\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_states\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Estate\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&kt\&\u003Euint32_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoCount\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Estruct\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_info\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoList\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[])\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&kt\&\u003Ebool\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&nb\&\u003Efalse\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Efor\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&kt\&\u003Euint32_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoCount\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E++\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EhasLoadMethods\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EheaderType\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EinfoList\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E].\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&nb\&\u003Etrue\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ebreak\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E!\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&nb\&\u003Enil\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&n\&\u003Erecursive_mutex_locker_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Elock\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EloadMethodLock\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003Erwlock_writer_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Elock2\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EruntimeLock\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Eload_images_nolock\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Estate\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoCount\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoList\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003Ecall_load_methods\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E();\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&nb\&\u003Enil\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Ch3\u003E什么是镜像\u003C\u002Fh3\u003E\u003Cp\u003E这里就会遇到一个问题:镜像到底是什么,我们用一个断点打印出所有加载的镜像:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&http:\u002F\u002Fpic3.zhimg.com\u002Fc1f5b45ae5ab3be9d7a47fd48f88b79a_b.jpg\& data-rawwidth=\&485\& data-rawheight=\&190\& class=\&origin_image zh-lightbox-thumb\& width=\&485\& data-original=\&http:\u002F\u002Fpic3.zhimg.com\u002Fc1f5b45ae5ab3be9d7a47fd48f88b79a_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E从控制台输出的结果大概就是这样的,我们可以看到镜像并不是一个 Objective-C 的代码文件,它应该是一个 target 的编译产物。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E...\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_info\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&err\&\u003E$\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E52\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff8a3C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFilePath\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff8a3C\u002Fspan\u003E \u003Cspan class=\&s\&\u003E\&\u002FSystem\u002FLibrary\u002FFrameworks\u002FCoreServices.framework\u002FVersions\u002FA\u002FCoreServices\&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFileModDate\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_info\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&err\&\u003E$\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E53\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff946dC\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFilePath\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff946dC\u002Fspan\u003E \u003Cspan class=\&s\&\u003E\&\u002Fusr\u002Flib\u002Fliblangid.dylib\&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFileModDate\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_info\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&err\&\u003E$\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E54\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff3C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFilePath\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff3C\u002Fspan\u003E \u003Cspan class=\&s\&\u003E\&\u002FSystem\u002FLibrary\u002FFrameworks\u002FFoundation.framework\u002FVersions\u002FC\u002FFoundation\&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFileModDate\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_info\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&err\&\u003E$\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E55\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0xC\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFilePath\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mh\&\u003E0x00007fff5fbff8f0\u003C\u002Fspan\u003E \u003Cspan class=\&s\&\u003E\&\u002FUsers\u002Fapple\u002FLibrary\u002FDeveloper\u002FXcode\u002FDerivedData\u002Fobjc-dibgivkseuawonexgbqssmdszazo\u002FBuild\u002FProducts\u002FDebug\u002Fdebug-objc\&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003EimageFileModDate\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这里面有很多的动态链接库,还有一些苹果为我们提供的框架,比如 Foundation、 CoreServices 等等,都是在这个 load_images 中加载进来的,而这些 imageFilePath 都是对应的\u003Cstrong\u003E二进制文件\u003C\u002Fstrong\u003E的地址。\u003C\u002Fp\u003E\u003Cp\u003E但是如果进入最下面的这个目录,会发现它是一个\u003Cstrong\u003E可执行文件\u003C\u002Fstrong\u003E,它的运行结果与 Xcode 中的运行结果相同:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&http:\u002F\u002Fpic4.zhimg.com\u002F25a2b5ed90aefbd_b.jpg\& data-rawwidth=\&994\& data-rawheight=\&299\& class=\&origin_image zh-lightbox-thumb\& width=\&994\& data-original=\&http:\u002F\u002Fpic4.zhimg.com\u002F25a2b5ed90aefbd_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cbr\u003E\u003Ch3\u003E准备 + load 方法\u003C\u002Fh3\u003E\u003Cp\u003E我们重新回到 load_images 方法,如果在扫描镜像的过程中发现了 + load 符号:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Efor\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&kt\&\u003Euint32_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E&\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoCount\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E++\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EhasLoadMethods\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EheaderType\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EinfoList\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E].\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&nb\&\u003Etrue\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ebreak\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E就会进入 load_images_nolock 来查找 load 方法:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-objective-c\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&kt\&\u003Ebool\u003C\u002Fspan\u003E \u003Cspan class=\&nf\&\u003Eload_images_nolock\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&k\&\u003Eenum\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_states\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Estate\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\u003Cspan class=\&kt\&\u003Euint32_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoCount\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Estruct\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Edyld_image_info\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoList\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[])\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&kt\&\u003Ebool\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Efound\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&nb\&\u003ENO\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&kt\&\u003Euint32_t\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EinfoCount\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ewhile\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E--\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Econst\u003C\u002Fspan\u003E \u003Cspan class=\&n\&\u003EheaderType\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Emhdr\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EheaderType\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EinfoList\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E[\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Ei\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E].\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EimageLoadAddress\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E!\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003EhasLoadMethods\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Emhdr\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))\u003C\u002Fspan\u003E \u003Cspan class=\&k\&\u003Econtinue\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n\n
\u003Cspan class=\&n\&\u003Eprepare_load_methods\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&n\&\u003Emhdr\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E}

我要回帖

更多关于 荒野行动黑夜模式 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信