mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
docs: fishhook 底层原理
This commit is contained in:
@@ -522,7 +522,7 @@ LinkMap 文件分为3部分:Object File、Section、Symbols。
|
||||
|
||||
Objective-C 中的方法都会通过 **objc_msgSend** 来调用,而 objc_msgSend 在 Mach-O 文件里是通过 **_objc_selrefs** 这个 **section** 来获取 selector 这个参数的。
|
||||
|
||||
所以,_objc_selrefs 里的方法一定是被调用了的。**_objc_classrefs** 里是被调用过的类, **objc_superrefs** 是调用过 super 的类(继承关系)。通过 _objc_classrefs 和 _objc_superrefs,我们就可以找出使用过的类和子类。
|
||||
里是被调用过的类, **objc_superrefs** 是调用过 super 的类(继承关系)。通过 _objc_classrefs 和 _objc_superrefs,我们就可以找出使用过的类和子类。
|
||||
|
||||
那么,Mach-O 文件中的 _objc_selrefs、_objc_classrefs、_objc_superrefs 如何查看呢?
|
||||
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
学过 React.js 之后你再去学习 React Native 会很简单,一些核心的东西理解之后会很简单。比如 React 中的单向数据流、虚拟 Dom、diff 算法、数据变动的批量更新机制、diff 之后的 UI 渲染。
|
||||
|
||||
|
||||
样式布局方面增加了 flexbox,这样子布局在移动端会非常方便,非常简单,
|
||||
样式布局方面增加了 flexbox,这样子布局在移动端会非常方便,非常简单,
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# 带你打造一套 APM 监控系统
|
||||
|
||||
内容脱敏中...
|
||||
File diff suppressed because it is too large
Load Diff
@@ -171,7 +171,13 @@ CI、CD 在稍微有点规模的公司内部都会内建一套自己的系统。
|
||||
旧版本 cocoapods 中:
|
||||
|
||||
- SDK、App 主工程都可以使用 `#import <NativeQS/NativeQS.h>`、`#import "NativeQS.h"`、`#import <NativeQS.h>`
|
||||
|
||||
2. 部分 SDK 的使用了未在 podspec 文件中声明的依赖,在新版本 cocoapods 下会报错(某些 SDK 由于历史原因造成新版本丢失依赖描述)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- 涉及到的 SDK:CMRCTToast
|
||||
- 改造点:
|
||||
问题基本定位是在于, App 主工程引用的 SdkBbs2 SDK 依赖了 SdkBbs2 版本(该版本的依赖描述为 `CMRCTToast (~> 0.1)` )
|
||||
@@ -190,6 +196,7 @@ CI、CD 在稍微有点规模的公司内部都会内建一套自己的系统。
|
||||
s.dependency 'CMDevice', '~> 0.1'
|
||||
```
|
||||
所以, 将 `CMRCTToast.podspec` 中的依赖修改掉。需要兼容不同 RN SDK 的版本。
|
||||
|
||||
3. 部分 pod 的 hook 脚本会失败。
|
||||
- 涉及到的 SDK:无
|
||||
- 改造点:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# 打造一个通用、可配置、多句柄的数据上报 SDK
|
||||
|
||||
内容脱敏中...
|
||||
@@ -355,7 +355,6 @@ HTTPStubsProtocol 继承自 NSURLProtocol,可以在 HTTP 请求发送之前对
|
||||
|
||||
|
||||
|
||||
[参考资料](https://github.com/draveness/analyze/blob/master/contents/OHHTTPStubs/如何进行%20HTTP%20Mock(iOS).md)
|
||||
|
||||
|
||||
|
||||
|
||||
100
Chapter1 - iOS/1.85.md
Normal file
100
Chapter1 - iOS/1.85.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# 统跳
|
||||
|
||||
|
||||
定义:
|
||||
|
||||
使用统一的 URL 来描述特定的业务功能,并通过路由映射到任意资源目标,并基于此可实现 rewrite、埋点、监控、灰度、A/B 等功能
|
||||
|
||||
|
||||
|
||||
iOS非统跳接口调用检测设计方案
|
||||
|
||||
工作原理
|
||||
|
||||
由于ObjectC语言自身的限制,获取编译时的调用关系信息相对较为困难。因此,没有一个简便的方法获取到哪些接口调用了SDK中的方法。
|
||||
|
||||
ObjectC语言继承自C语言的头文件引用的机制,可以作为检测的点。即,如果App中调用了SDK中的某个方法,那么一定会直接或者间接引用了SDK中的某个头文件。通过检测引用头文件,引导iOS开发删除对应SDK的头文件,那么应该可以暴露出App中直接调用SDK接口的位置。从而达到辅助检测非统跳接口的目的。
|
||||
|
||||
|
||||
配置文件
|
||||
配置文件主要用于在检测过程中,提供工程的目录结构相关信息。在生成过程中,提供生成结果文件需要的额外信息。
|
||||
|
||||
```YAML
|
||||
category: login_register_sdk
|
||||
plist: Example/LoginRegisterSDK/LoginRegisterSDK-Info.plist
|
||||
workspace: Example/LoginRegisterSDK.xcworkspace
|
||||
xcodeproj: Example/LoginRegisterSDK.xcodeproj
|
||||
scheme: LoginRegisterSDK-Example
|
||||
source_directory: LoginRegisterSDK/
|
||||
pods_directory: Example/Pods
|
||||
ignore:
|
||||
- LoginRegisterSDK/**/*SDK.m
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
调用流程
|
||||
|
||||
```objective-c
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// ...
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[TNTRouter sharedRouter] manuallyFetchRoutingTableWithCompletion:^(BOOL succeeded) {
|
||||
|
||||
}];
|
||||
});
|
||||
|
||||
return YES;
|
||||
}
|
||||
```
|
||||
|
||||
```objective-c
|
||||
- (void)manuallyFetchRoutingTableWithCompletion:(TNTManualFetchRoutingTableCompletion)completion {
|
||||
NSDictionary *params = [self getRouterRequestParams];
|
||||
[TMTrinityMGetTask manuallyFetchWithKey:TMNeutronRouterRewriteKey
|
||||
params:params
|
||||
moduleCallback:^(NSString * _Nullable result, NSError * _Nullable error) {
|
||||
if (error) return ;
|
||||
if (result.length == 0) return;
|
||||
|
||||
// encryp & json
|
||||
NSDictionary *routesAndWrites = [self routesAndRewriteForRemoteEncryptedString:result];
|
||||
|
||||
// routes is existed
|
||||
if (routesAndWrites) {
|
||||
// process routes
|
||||
[self loadRoutesAndWritesFromRemote:routesAndWrites];
|
||||
|
||||
// save decrpy data string
|
||||
[self saveRemoteRouteString:result];
|
||||
|
||||
if(completion) completion(true);
|
||||
}
|
||||
} callback:nil];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 参考资料
|
||||
|
||||
- [iOS performSelector传递两个以上参数](https://www.jianshu.com/p/4118793c88df)
|
||||
-
|
||||
|
||||
|
||||
之前看代码理解设计太慢了,不清楚设计背景,所以想请教你一下
|
||||
|
||||
1. 使用统一的 URL 来描述特定的业务功能,并通过路由映射到任意资源目标,并基于此可实现 rewrite、埋点、监控、灰度、A/B 等功能
|
||||
如何理解rewrite、埋点、监控、灰度、A/B 等功能。
|
||||
|
||||
用统跳 url 后,调用某 SDK ,区分 type,h5、RN、Native,native 则 runtime,performSelector,
|
||||
H5、RN 没看到处理逻辑?
|
||||
|
||||
2. 通跳的设计背景是什么?解决业务上什么问题?举个例子理解下工作流程
|
||||
- RoutingTableGenerator.rb 扫描工程 TNT_TARGET,写入 Targets.json 的目的是什么?
|
||||
- DefaultRoutingTableFetcher.rb 调用接口,写入文件
|
||||
- ProjectConfigurator.rb 调用上述2个脚本,写入工程 script
|
||||
|
||||
3. TrinityParams 和 TrinityConfigurator 2个 SDK 背景、作用分别是什么?
|
||||
7
Chapter1 - iOS/1.86.md
Normal file
7
Chapter1 - iOS/1.86.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# GCD 源码探究
|
||||
|
||||
|
||||
|
||||
可以设计实现一个线程池,涉及几个角色、任务队列、调度者如何调度、线程池的有哪些策略。iOS GCD 的线程池策略可以类比 Java 中的4个线程池策略,明白不同语言设计的共同之处
|
||||
|
||||
- https://juejin.im/post/6855807995570618375
|
||||
157
Chapter1 - iOS/1.87.md
Normal file
157
Chapter1 - iOS/1.87.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Objective-C 底层
|
||||
|
||||
1. Objective-C 中对象、类主要是基于 C/C++ 中的结构体实现的。
|
||||
|
||||
方法一:
|
||||
|
||||
可以用 clang 验证。`clang -rewrite-objc main.m -o main.cpp`
|
||||
转到指定平台代码。`xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp`
|
||||
|
||||
```
|
||||
struct NSObject_IMPL {
|
||||
Class isa;
|
||||
};
|
||||
|
||||
/// An opaque type that represents an Objective-C class.
|
||||
typedef struct objc_class *Class;
|
||||
```
|
||||
|
||||
所以 Class 是指向结构体的指针,所以在 64 位系统中占据8个字节,32位系统占据4个字节。
|
||||
|
||||
NSObject 就是结构体占据8个字节。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
发现 `class_getInstanceSize` 和 `malloc_size` 结果不一致。
|
||||
|
||||
```c
|
||||
// Class's ivar size rounded up to a pointer-size boundary.
|
||||
uint32_t alignedInstanceSize() const {
|
||||
return word_align(unalignedInstanceSize());
|
||||
}
|
||||
```
|
||||
|
||||
`class_getInstanceSize` 返回的是类的实例对象的成员变量的大小(四舍五入等于指针指向对象的大小,所以不精确)
|
||||
|
||||
```c
|
||||
extern size_t malloc_size(const void *ptr);
|
||||
/* Returns size of given ptr */
|
||||
```
|
||||
|
||||
`malloc_size` 返回的就是指针指向的对象大小.
|
||||
|
||||

|
||||
|
||||
结论:
|
||||
|
||||
- 当某个类继承自 NSObject 的时候,如果没有其他属性,则这个类占据16个字节。`class_getInstanceSize` 占据 8 , `malloc_size` 占据 16
|
||||
- 当某个类继承自 NSObject 的时候,如果有其他属性,则这个类占据16个字节。`class_getInstanceSize` 占据 16 , `malloc_size` 占据 16
|
||||
|
||||
方法二: 从源代码角度出发验证(从上大下)
|
||||
|
||||
```c++
|
||||
// NSObject.mm
|
||||
// Replaced by ObjectAlloc
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone {
|
||||
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
|
||||
}
|
||||
|
||||
// objc-class-old.mm
|
||||
id
|
||||
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
|
||||
{
|
||||
id obj;
|
||||
|
||||
if (fastpath(!zone)) {
|
||||
obj = class_createInstance(cls, 0);
|
||||
} else {
|
||||
obj = class_createInstanceFromZone(cls, 0, zone);
|
||||
}
|
||||
|
||||
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// objc-class-old.mm
|
||||
/***********************************************************************
|
||||
* _class_createInstance. Allocate an instance of the specified
|
||||
* class with the specified number of bytes for indexed variables, in
|
||||
* the default zone, using _class_createInstanceFromZone.
|
||||
**********************************************************************/
|
||||
static id _class_createInstance(Class cls, size_t extraBytes)
|
||||
{
|
||||
return _class_createInstanceFromZone (cls, extraBytes, nil);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ALWAYS_INLINE id
|
||||
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
|
||||
int construct_flags = OBJECT_CONSTRUCT_NONE,
|
||||
bool cxxConstruct = true,
|
||||
size_t *outAllocatedSize = nil)
|
||||
{
|
||||
ASSERT(cls->isRealized());
|
||||
|
||||
// Read class's info bits all at once for performance
|
||||
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
|
||||
bool hasCxxDtor = cls->hasCxxDtor();
|
||||
bool fast = cls->canAllocNonpointer();
|
||||
size_t size;
|
||||
|
||||
size = cls->instanceSize(extraBytes);
|
||||
if (outAllocatedSize) *outAllocatedSize = size;
|
||||
|
||||
id obj;
|
||||
if (zone) {
|
||||
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
|
||||
} else {
|
||||
obj = (id)calloc(1, size);
|
||||
}
|
||||
if (slowpath(!obj)) {
|
||||
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
|
||||
return _objc_callBadAllocHandler(cls);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!zone && fast) {
|
||||
obj->initInstanceIsa(cls, hasCxxDtor);
|
||||
} else {
|
||||
// Use raw pointer isa on the assumption that they might be
|
||||
// doing something weird with the zone or RR.
|
||||
obj->initIsa(cls);
|
||||
}
|
||||
|
||||
if (fastpath(!hasCxxCtor)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
|
||||
return object_cxxConstructFromClass(obj, cls, construct_flags);
|
||||
}
|
||||
|
||||
// objc-runtime-new.h
|
||||
// Class's ivar size rounded up to a pointer-size boundary.
|
||||
uint32_t alignedInstanceSize() const {
|
||||
return word_align(unalignedInstanceSize());
|
||||
}
|
||||
|
||||
// objc-runtime-new.h
|
||||
size_t instanceSize(size_t extraBytes) const {
|
||||
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
|
||||
return cache.fastInstanceSize(extraBytes);
|
||||
}
|
||||
|
||||
size_t size = alignedInstanceSize() + extraBytes;
|
||||
// CF requires all objects be at least 16 bytes.
|
||||
if (size < 16) size = 16;
|
||||
return size;
|
||||
}
|
||||
```
|
||||
|
||||
`CF requires all objects be at least 16 bytes.` 系统为 NSObject 对象分配了至少16个字节大小的空间,但是它使用了 8 个字节大小的空间用来存放 ivars(64位系统)
|
||||
|
||||
2. 某个类继承自 NSObject 的情况,内存如何分配
|
||||
40
Chapter1 - iOS/1.88.md
Normal file
40
Chapter1 - iOS/1.88.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# fishhook 原理
|
||||
|
||||
1. image: 代码编译后的可执行文件,被加载到内存中,就叫做镜像文件。
|
||||
|
||||
2. MachO 结构:
|
||||
|
||||
3. iOS 代码在编译时没有办法确定方法的实现地址。**动态库共享缓存**,里面有动态库。NSLog 属于 Foundation 框架,每个手机内部中的地址不一定。
|
||||
|
||||

|
||||
|
||||
4. DYLD 动态链接器,负责将可执行文件加载到内存中。App 启动后,DYLD 将 Foundation、UIKit 等加载进动态库共享缓存中,但是加载到的位置不确定。
|
||||
|
||||
5. 可执行文件头有 **Load Commands**:加载命令。告诉 DYLD 依赖了什么。
|
||||
|
||||
6. 从 NSLog 找到代码实现,经历过2种方式。早期:重定向。现在:PIC 技术(位置代码独立)。
|
||||
|
||||
7. 可执行文件:
|
||||
|
||||
- 代码段:可读可执行
|
||||
- 数据段:可读可写
|
||||
|
||||
8. 写的业务代码里面假如某一行调用了 `NSLog`,那么在编译阶段,使用 NSLog 只是 IDE 提供了功能,让你可以看到声明而已。编译后的可执行文件,还是不知道 NSLog 的具体函数地址,它指向了一个表(类似一个应用程序的外部函数名,函数真正实现地址),去这个表里面找地址,这个表叫做**符号表**。
|
||||
|
||||
当 DYLD 加载当前可执行文件的时候,才将这个表每个编号对应的函数地址去填上去,这个动作叫做**符号绑定**。
|
||||
|
||||
当真正去调用 NSLog 函数的时候才去这个符号表中去寻找函数地址,去调用实现。
|
||||
|
||||
| 编号(符号) | 地址 | |
|
||||
| ------------ | -------- | ---- |
|
||||
| NSLog | 0xaabbcc | |
|
||||
| ... | ... | |
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
9. fishhook 做的事情就是将系统的符号表,将符号表中的特定符号对应的地址,修改为自定义的函数地址。起到了 hook 作用。也就是说外部的 c 函数,在 iOS 中的调用属于**动态调用**。
|
||||
|
||||
https://www.bilibili.com/video/BV1UZ4y1u7Ba?from=search&seid=14997461811427810898
|
||||
|
||||
@@ -87,4 +87,8 @@
|
||||
* [81、__asm__ 重命名符号](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.81.md)
|
||||
* [82、runtime](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.82.md)
|
||||
* [83、NSURLProtocol 应用场景](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.83.md)
|
||||
* [84、WKWebView 技巧集合](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.84.md)
|
||||
* [84、WKWebView 技巧集合](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.84.md)
|
||||
* [85、统跳技术](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.85.md)
|
||||
* [86、GCD 源码探究](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.86.md)
|
||||
* [87、Objective-C 底层探究](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.87.md)
|
||||
* [88、fishhook 原理](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.88.md)
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
|
||||
React 性能高效的一个原因就是 Virtual Dom 的应用和 diff 之后的 Batch Update(批量处理,类比 Vue 中的 $nextTick。有 Native 开发经验的同学对于这里应该有似曾相识的感觉,和 RunLoop 很像。任何 UI 层变动的东西提交给系统,系统再下一次的运行循环到来的时候统一去渲染。)
|
||||
|
||||
- https://www.infoq.cn/article/2iviqjklwa4JkF0YNQGZ
|
||||
|
||||
- https://www.infoq.cn/article/AiQMbjI0oXZ1UrueiBze
|
||||
|
||||
|
||||
|
||||
## Diff 算法
|
||||
|
||||
@@ -235,4 +239,3 @@ class Welcome extends React.Component {
|
||||
- Vue 设计思想:How easy it can be。React:How corrct it can be 和 all in js(css写法也在用 js 控制,比如 styled-component)
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@ app.on('activate', function () {
|
||||
|
||||
app 对象在 `whenReady` 的时候执行 createWindow 方法。内部创建了一个 `BrowserWindow` 对象,指定了大小和功能设置。
|
||||
|
||||
1. webPreferences Object (可选) - 网页功能的设置。
|
||||
1. webPreferences Object (可选) - 网页功能的设置。
|
||||
|
||||
2. preload String (可选) - 在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成 Node, 此脚本都可以访问所有 Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入 node 的全局引用标志。
|
||||
|
||||
`mainWindow.loadFile('index.html')` 加载了同级目录下的 index.html 文件。也可以加载服务器资源(部署好的网页),比如 `win.loadURL('https://github.com/FantasticLBP')`
|
||||
@@ -143,7 +144,7 @@ electron 分为**渲染进程和主进程**。和 Native 中的概念不一样
|
||||
|
||||
- 问题1: 不稳定
|
||||
|
||||
早期浏览器需要借助插件来实现类似 Web 视频、Web 游戏等各种“强大”的功能。但插件往往是最容易出现问题的模块。此外因为运行在浏览器进程中,所以一个插件的意外奔溃到导致整个浏览器到的奔溃。
|
||||
早期浏览器需要借助插件来实现类似 Web 视频、Web 游戏等各种“强大”的功能。但插件往往是最容易出现问题的模块。因为运行在浏览器进程中,所以一个插件的意外奔溃到导致整个浏览器到的奔溃。
|
||||
|
||||
除了插件之外,**渲染引擎模块也是不稳定的**。通常一些复杂的 Javascript 代码就有可能导致渲染引擎模块的奔溃。和插件一样,渲染引擎的奔溃会导致整个浏览器奔溃。
|
||||
|
||||
@@ -184,8 +185,6 @@ electron 分为**渲染进程和主进程**。和 Native 中的概念不一样
|
||||
|
||||
对于**内存泄漏的解决办法更加简单**。当关闭某个页面的时候,整个渲染进程就会被关闭,所以该进程所占用的内存都会被系统回收,于是轻松解决了浏览器页面的内存泄漏问题。
|
||||
|
||||
|
||||
|
||||
**解决了安全问题。**采用多进程架构可以使用**安全沙箱技术**。沙箱可以看成是操作系统给浏览器一个小黑盒,黑盒内部可以执行程序,但是不能访问操作系统资源、不能访问硬盘数据,也不能在敏感位置读取任何数据,例如你的文档和桌面。Chrome 把插件进程和渲染进程使用沙箱隔离起来,这样即使在渲染进程或者浏览器进程中执行了恶意代码,恶意代码也无法突破沙箱限制去获取系统权限。
|
||||
|
||||
沙箱隔离起来的进程必须使用 IPC 通道才可以与浏览器内核进程通信,通信进程就会进行安全的检查。
|
||||
|
||||
@@ -21,10 +21,3 @@
|
||||
## 反馈
|
||||
|
||||
定期更新博文。如果在查看文章的时候发现了问题可以提出 issue。(95年小双鱼,关注大前端领域,有事情可以通过[微博](http://weibo.com/u/3194053975)联系)
|
||||
|
||||
|
||||
|
||||
|
||||
## 交流
|
||||
|
||||
如果你也是大前端路上的一名修行者,可以通过[微博](http://weibo.com/u/3194053975)联系我,拉你入群一起在「大前端自习室」学习交流。
|
||||
@@ -87,6 +87,10 @@
|
||||
* [82、runtime](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.82.md)
|
||||
* [83、NSURLProtocol 应用场景](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.83.md)
|
||||
* [84、WKWebView 技巧集合](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.84.md)
|
||||
* [85、统跳技术](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.85.md)
|
||||
* [86、GCD 源码探究](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.86.md)
|
||||
* [87、Objective-C 底层探究](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.87.md)
|
||||
* [88、fishhook 原理](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.88.md)
|
||||
|
||||
* [Chapter2 - Web FrontEnd](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/chapter2.md)
|
||||
* [1、-last-child与-last-of-type你只是会用,有研究过区别吗?](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/2.1.md)
|
||||
@@ -177,7 +181,7 @@
|
||||
* [17、一套开发规范](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.18.md)
|
||||
* [18、云服务器靠谱推荐](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.19.md)
|
||||
* [19、规范化团队 git 提交信息](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.20.md)
|
||||
* [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md)
|
||||
* [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md)
|
||||
|
||||
|
||||
* [Chapter8 - Finance](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter8%20-%20Finance/chapter8.md)
|
||||
|
||||
BIN
assets/image-20200810182447587.png
Normal file
BIN
assets/image-20200810182447587.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
BIN
assets/image-20200810190329801.png
Normal file
BIN
assets/image-20200810190329801.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 KiB |
Reference in New Issue
Block a user