纳兰若水


  • 首页

  • 归档

  • 分类

  • 标签

runloop

发表于 2019-08-01 | 分类于 iOS
字数统计: 1.8k | 阅读时长 ≈ 7

深入理解 runloop

本文参考了多位前辈的文章、视频和源码进行学习、以及总结加深理解。

  • iOS线下分享《RunLoop》by 孙源@sunnyxx
  • 深入理解RunLoop
    RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念。runloop是与线程相关的基础架构的一部分。runloop是指用于安排工作,并协调接收传入事件的事件处理循环。runloop的目的是在有工作时保持线程忙,并在没有工作时时让线程进入休眠状态。本文从源码入手,理解 runloop 原理,以及相关自动释放池、延迟回调、触摸事件、屏幕刷新等功能。

RunLoop 概念

一般来讲,一个线程一次只执行一个任务,任务执行完成后线程就退出了,runloop 就是能让线程保持随时能处理任务但不退出的一个机制。这种机制就是 Event Loop 模型,实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。

OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。

RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。这种说法比较抽象,下面会结合开发过程中使用到的例子来配合源码进行理解。

阅读全文 »

【译】A taste of MVVM and Reactive paradigm

发表于 2019-06-26
字数统计: 4.4k | 阅读时长 ≈ 18

【译】A taste of MVVM and Reactive paradigm

Medium 原文 A taste of MVVM and Reactive paradigm
原文博客 A taste of MVVM and Reactive paradigm

我喜欢 Swift,就像许多其他面向对象的编程语言一样。 Swift 允许你表示具有某些特点和执行一些操作的真实世界对象。

我倾向于认为 App 是一个每个对象都是一个人的世界。他们工作和沟通。如果一个人不能独自完成工作,他需要寻求帮助。举一个项目,例如,如果经理必须自己完成所有的工作,他会发疯的。因此需要组织和委派任务,并且需要许多人在项目上进行协作:设计师,测试人员,Scrum 主管,开发人员。任务完成后,需要通知经理。

这可能不是一个好例子。但至少你了解 OOP 中沟通和授权的重要性。当我开始 iOS 编程时,我对“架构”一词非常感兴趣。但在做了一段时间后,这一切都归结为识别和分担责任。本文讲述了 MVC 和 MVVM 的简单 Extract 类重构,以及如何进一步研究 Rx。您可以自由地创建自己的架构,但无论您做什么,一致性都是关键,不要让您的队友感到困惑或惊讶。

MVC

看看你最熟悉的架构 - MVC,模型视图控制器的简称。 在新建一个 iOS 项目时总是会得到一个这样的架构。 View 是您使用 UIView,UIButton,UILabel 呈现数据的位置。 Model 只是数据的一个设想的词。 它可以是您的实体,来自网络的数据,来自数据库的对象或来自缓存。Controller 是在 Model 和 View 间进行调解的东西。

宇宙中心 - UIViewController

ViewController 的问题在于它往往是巨大的。 Apple 把它作为宇宙的中心,它拥有许多属性和责任。你可以用 UIViewController 做很多事情。诸如与故事板交互,管理视图,配置视图轮换,状态恢复等事情。 UIViewController 设计了很多可以覆盖和自定义的方法。

阅读全文 »

app 加载过程 & 启动速度优化

发表于 2019-02-25 | 分类于 iOS
字数统计: 1.8k | 阅读时长 ≈ 6

上篇文章介绍了 MachO 文件的结构,你可能注意到其中的 LC_LOAD_DYLINKER 是 dyld, LC_MAIN 加载命令就是加载程序的主入口。这篇文章就详细讲讲 App 的加载过程。

MachO 可执行文件类型

Xcode build 出的 .app 包中可以看到一个 exec 可执行文件(所有 .o文件集合),同样是一个 MachO 文件,filetype 就是 MH_EXECUTE 类型。
MachOView中查看如下。

阅读全文 »

MachO 文件结构分析

发表于 2019-02-23 | 分类于 iOS
字数统计: 3.2k | 阅读时长 ≈ 16

上一篇文章中提到了如何自建一个 Crash 平台,其中通过对系统库 (MachO) 的结构解析来寻找崩溃符号。这篇文章就具体讲讲 MachO 文件的结构分析。

iOS中,我们平时看见的 MachO 文件你肯定不陌生,包括静态库(.a)、dSym (yourAppName.dSym)、系统动态库 (/usr/lib/libobjc.A.dylib)、可执行文件等。具体类型下面会讲到。

MachO 二进制文件可以根据前四字节的magic_num来判断是不是 Fat (包含一个或多个架构,有 Fat_Header), 每个架构同样是的 MachO文件。可以这样比喻,相当于对一个或多个文件用文件夹压缩了下。zip 包相当于 Fat,文件是 Thin。每个文件的内部结构式一致的。

Fat


可以看到 Fat 多了 Fat_Header信息, 信息中包含架构数,每个架构的基本信息。
Fat 可以通过lipo -thin 命令分解出 thin。 thin 也可以合并成 Fat。

1
2
3
4
5
//分解
lipo BICrashAnalyzeDemo -thin arm64 -output crashAnalyzeDemoARM64
lipo BICrashAnalyzeDemo -thin armv7 -output crashAnalyzeDemoARMV7
//合并
lipo crashAnalyzeDemoARM64 crashAnalyzeDemoARMV7 -create -output BICrashAnalyzeDemo
阅读全文 »

如何自建 Crash 平台

发表于 2019-02-20 | 分类于 iOS
字数统计: 4.7k | 阅读时长 ≈ 22

【译】Symbolicating an iOS Crash Report

通常,当您收到来自iTunes连接的崩溃报告或提供移动崩溃收集和报告的第三方服务(如Apteligent)时,该服务将负责为您提供符号化后的崩溃。如果你没有上传符号,你可能会发现自己有一个非符号化的崩溃,没有别的东西可以继续。这样的崩溃文件对于调试可能影响大量用户的问题并不是非常有用。

在这种情况下,您必须通过将回溯堆栈地址解析为符号来对崩溃报告进行符号化,以获取有关崩溃的有用信息。

幸运的是,完全有可能手动符号化崩溃报告。本文将概述您需要的信息,向您展示如何解释崩溃报告,并查看OSX和XCode上可用的一些工具来符号化崩溃。

崩溃报告中只有两个部分与符号化异常跟踪相关。第一个是 Exception Backtrace 部分。这显示了崩溃时应用程序的调用堆栈。此特定崩溃日志片段显示了我们的 ApteligentExampleApp 应用程序内部崩溃的回溯。

1
2
3
4
5
Last Exception Backtrace:
0 CoreFoundation 0x000000018708b100 0x186f80000 + 1093888
1 libobjc.A.dylib 0x00000001939441fc 0x19393c000 + 33276
2 CoreFoundation 0x000000018708b040 0x186f80000 + 1093696
3 ApteligentExampleApp 0x000000010003acc4 0x10002c000 + 60612
阅读全文 »

block 探索

发表于 2019-02-10 | 分类于 iOS
字数统计: 2.1k | 阅读时长 ≈ 9

block 转 c++ 源码

如下代码 .h .m文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//TestClang.h
@interface TestClang : NSObject
+ (void)testBlcok;
@end

//TestClang.m
#import "TestClang.h"
static int numGlobel = 29;
@implementation TestClang
+ (void)testBlcok {
//没有截获局部变量 __NSGlobalBlock__
void(^block1)(void) = ^{
NSLog(@"just a block");
    };
NSLog(@" block1 = %@", block1);
block1();
static int numStatic = 12;
int num = 10;
__block int numBlock = 19;
__block int numBlock2 = 30;
__block int numBlockTest = 30;
    void(^block2)(void) = ^{
NSLog(@"just a block === %d, numStatic = %d numGlobel = %d numBlock=%d numBlock2=%d numBlockTest = %d", num,numStatic,numGlobel,numBlock,numBlock2,numBlockTest);
    };
num = 33;
numStatic = 121;
numGlobel = 129;
numBlock = 22222;
block2();
NSLog(@"block2 = %@", block2);
}
@end

执行:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc TestClang.m
不要引用其他头文件,以免导出报Error
目录下生成了一个TestClang.cpp文件

  • testBlock 对应的方法变为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//_C_ 表示为类方法 _I_ 为实例方法
static void _C_TestClang_testBlcok(Class self, SEL _cmd) {

void(*block1)(void) = ((void (*)())&__TestClang__testBlcok_block_impl_0((void *)__TestClang__testBlcok_block_func_0, &__TestClang__testBlcok_block_desc_0_DATA));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_cf514e_mi_1, block1);
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
static int numStatic = 12;
int num = 10;
__attribute__((__blocks__(byref))) __Block_byref_numBlock_0 numBlock = {(void*)0,(__Block_byref_numBlock_0 *)&numBlock, 0, sizeof(__Block_byref_numBlock_0), 19};
__attribute__((__blocks__(byref))) __Block_byref_numBlock2_1 numBlock2 = {(void*)0,(__Block_byref_numBlock2_1 *)&numBlock2, 0, sizeof(__Block_byref_numBlock2_1), 30};
__attribute__((__blocks__(byref))) __Block_byref_numBlockTest_2 numBlockTest = {(void*)0,(__Block_byref_numBlockTest_2 *)&numBlockTest, 0, sizeof(__Block_byref_numBlockTest_2), 30};

    void(*block2)(void) = ((void (*)())&__TestClang__testBlcok_block_impl_1((void *)__TestClang__testBlcok_block_func_1, &__TestClang__testBlcok_block_desc_1_DATA, num, &numStatic, (__Block_byref_numBlock_0 *)&numBlock, (__Block_byref_numBlock2_1 *)&numBlock2, (__Block_byref_numBlockTest_2 *)&numBlockTest, 570425344));
num = 33;
numStatic = 121;
numGlobel = 129;
(numBlock.__forwarding->numBlock) = 22222;
((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_cf514e_mi_3, block2);
}
阅读全文 »

武功山游记

发表于 2018-12-31 | 分类于 游记
字数统计: 988 | 阅读时长 ≈ 3

续:2018年12月31日,雪后,不一样的武功山~

一直想去看冬季的武功山,期待满山白雪皑皑,雪景连接云海。何等壮观。我提出冬季上武功山露营,一家人心痒痒,但是担心大雪封山,临回家才确定上山。但是考虑到露营太冷,改为山脚景区泡温泉,次日上山。抵达景区温泉还下着雪,就着雪泡温泉的感觉真是冰火两重天。泡够时间就 “落荒而逃” ~

次日9点到达武功山脚下,买好门票缆车票。得知山中 -6℃ ,又买好口罩手套等。脚踩防滑链,踏着积雪,向着被白雪覆盖的武功山前进。

阅读全文 »

on-demand 资源下发

发表于 2018-12-06 | 分类于 iOS
字数统计: 576 | 阅读时长 ≈ 2

Apple On-Demand Resource

Xcode 可以给资源打 tag,被打 tag 的资源在打包时不会打到包内。需要时下载,测试时可以使用自己的服务器,但是发布到 AppStore 时需要托管到苹果的服务器。
苹果的 On-Demand Resource 文档

自建资源下发模块

了解到了苹果 On-Demand Resource 机制,我们可以结合Resource Tags 下的资源不会打包到 ipa 包的特性。设计一套自己的下发机制。

阅读全文 »

xcodeproj

发表于 2018-11-30 | 分类于 工具
字数统计: 419 | 阅读时长 ≈ 1

xcodeproj

xcodeproj 实际上是一个文件夹,打开后可以看到 project.pbxproj, xcuserdata, project.xcworkspace.

  • project.pbxproj: “在 project 里的 project”,是 xcodeproj 最主要的文件
  • project.xcworkspace: 一个子文件夹,存储了用户 workspace 的相关信息
  • xcuserdata: 另外一个文件夹,里面也是用户相关的一些信息
阅读全文 »

冲绳四日三晚游记

发表于 2018-09-11 | 分类于 游记
字数统计: 2.8k | 阅读时长 ≈ 9

行程时间: 9月7日 - 9月10日
提前一个月的时间定了吉祥航空的往返航班,2700左右/人。出发时下午5点,回程是下午7点。
行程酒店:美国村门巴公寓酒店,酒店外就是沙滩。
冲绳9月份是台风季,天气说不准,最好提前做下攻略。

Day 1

启程,第一天出发的时间是北京时间5点,到冲绳大概2个小时,不过因为冲绳是早一个小时的,所以到冲绳是当地时间晚上8点了。预订的接机去美国村的酒店,400RMB。
抵达冲绳从机场出来就感觉非常干净,各种建筑街道啥的都非常干净,各种基础设施做的也很贴心。约的日本的司机来接机的,人很有礼貌,会一点英语, 大概花了50分钟左右到美国村。入住之后就去觅食,找到了上一波推荐去的一家叫 xx食堂的店,吃了猪豚骨饭,另外在冲绳大众点评也挺好用(^__^)。
美国村夜景

阅读全文 »
123…5
纳兰若水

纳兰若水

随便写写读书笔记、技术学习、随想、生活、游记

41 日志
8 分类
5 标签
RSS
GitHub Twitter
© 2017 — 2024 纳兰若水
沪ICP备2021021008号-1
| Site words total count: 0 字 Site words total count: 73.3k