├── .gitignore ├── images ├── full.png ├── run.png ├── debug.png ├── detail.png ├── compile.png ├── setting.png ├── page_table.png ├── address_space.png ├── architecture_mvp.png ├── architecture_mvvm.png ├── architecture_viper.png ├── ca_architecture_new.png ├── ca_architecture_old.png ├── page_fault_of_linux.png ├── architecture_actual_mvc.png ├── architecture_apple_mvc.png ├── core_animation_pipline.png ├── architecture_observing_mvp.png ├── macos_project_screen_shot.png ├── virtual_memory_of_linux_01.png ├── virtual_memory_of_linux_02.png └── architecture_traditional_mvc.png ├── Compiling ├── Make_tool_tips.md ├── Visual_studio_code_tips.md ├── C&C++ tips.md ├── Traditional headermap style is no longer supported.md ├── How_to_ cross_compiled_osip2_and_eXosip2_with_Mac.md └── Xcode_command_line_tools .md ├── Linux ├── Read_source_code.md └── Ubuntu_tips.md ├── OSFoundation ├── programming_tips.md ├── About_locks_II.md ├── Virtual_memory.md ├── About_locks_I.md ├── Exception_catch.md └── Exceptional_Control_Flow.md ├── README.md ├── iOS ├── Work_Flow_Of_Hotspot_Helper.md ├── Tool_tips.md ├── Something_more_about_off_screen_rendering.md ├── How_to_change_your_Icon_in_code .md ├── How_to_decrypt_a_iOS_App.md ├── What_is_a_meta-class_in_Objective-C.md ├── Pattern_matching_in_Swift.md └── Swift_Tips.md ├── Interview_questions ├── Interview_questions_about_database.md ├── Interview_questions_about_memory_management.md ├── Interview_questions_about_Swift.md ├── Interview_questions_about_third_library.md ├── Interview_questions_about_java_from_ali.md ├── question about iOS.txt ├── Interview_questions_about_operating_systems.md ├── Interview_questions_about_compiling.md ├── Interview_questions_about_design_patterns.md ├── Interview_questions_about_objc_runtime.md ├── Interview_questions_about_data_structures.md └── Interview_questions_about_net_protocol.md ├── macOS ├── update_homebrew_remote_source.md └── macOS_project_without_xib_or_storyboard.md ├── Ethereum ├── Structure_of_Ethereum_Keystore_file.md ├── How_to_build_a_private_blockchain_on_Mac.md ├── Layout_of_a_solidity_source_file.md └── Browser-solidity_Installation_&_Introduction.md ├── Non-atomic_vs_atomic.md ├── Python ├── Flask │ ├── Flask_notes_02.md │ ├── Flask_notes_03.md │ └── Flask_notes_01.md └── Python_notes.md ├── Solidity └── Solidity_in_depth_-_Structure_of_a_Contract.md ├── Java ├── Java_Anotations.md └── All_about_Java.md └── OpenCV ├── OpenCV_base_types.md └── OpenCV_images_and_large_array_types.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /images/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/full.png -------------------------------------------------------------------------------- /images/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/run.png -------------------------------------------------------------------------------- /images/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/debug.png -------------------------------------------------------------------------------- /images/detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/detail.png -------------------------------------------------------------------------------- /images/compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/compile.png -------------------------------------------------------------------------------- /images/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/setting.png -------------------------------------------------------------------------------- /images/page_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/page_table.png -------------------------------------------------------------------------------- /images/address_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/address_space.png -------------------------------------------------------------------------------- /images/architecture_mvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_mvp.png -------------------------------------------------------------------------------- /images/architecture_mvvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_mvvm.png -------------------------------------------------------------------------------- /images/architecture_viper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_viper.png -------------------------------------------------------------------------------- /images/ca_architecture_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/ca_architecture_new.png -------------------------------------------------------------------------------- /images/ca_architecture_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/ca_architecture_old.png -------------------------------------------------------------------------------- /images/page_fault_of_linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/page_fault_of_linux.png -------------------------------------------------------------------------------- /images/architecture_actual_mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_actual_mvc.png -------------------------------------------------------------------------------- /images/architecture_apple_mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_apple_mvc.png -------------------------------------------------------------------------------- /images/core_animation_pipline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/core_animation_pipline.png -------------------------------------------------------------------------------- /images/architecture_observing_mvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_observing_mvp.png -------------------------------------------------------------------------------- /images/macos_project_screen_shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/macos_project_screen_shot.png -------------------------------------------------------------------------------- /images/virtual_memory_of_linux_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/virtual_memory_of_linux_01.png -------------------------------------------------------------------------------- /images/virtual_memory_of_linux_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/virtual_memory_of_linux_02.png -------------------------------------------------------------------------------- /images/architecture_traditional_mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dracarys/Articles/HEAD/images/architecture_traditional_mvc.png -------------------------------------------------------------------------------- /Compiling/Make_tool_tips.md: -------------------------------------------------------------------------------- 1 | # make tool tips 2 | 3 | ## Makefile 中的几种赋值 4 | 5 | makefile 中几种赋值的区别: 6 | 7 | - =  延时变量,只有被使用时才展开定义 8 | - := 立即变量,定义时的赋值立即有效 9 | - ?= 条件变量,当变量为空时才赋值 10 | - += 追加赋值 -------------------------------------------------------------------------------- /Compiling/Visual_studio_code_tips.md: -------------------------------------------------------------------------------- 1 | ## Visual studio code tips 2 | 3 | ### 头文件引入错误 4 | VS Code 的头文件引入错误会向上冒泡,例如:假设A文件引入了B,B又引入了C,C又引入了D,D引入了“mysql.h”。而此时的linux中尚未安装“libmysqlclient-dev”,那么该引入错误逐层上浮D->C->B->A, 5 | 直至顶层。所以在排查是最好仔细查看错误提示,不明白微软为什么如此设计。 6 | -------------------------------------------------------------------------------- /Linux/Read_source_code.md: -------------------------------------------------------------------------------- 1 | # 如何阅读源码 2 | 3 | 实际在过代码的时候,其实我们没有必要对每一个变量和每一行代码咬文嚼字,也不建议这样去做,我们只需要重点关注主要的数据成员和那些关键的代码行,把心思和精力投入到我们最该关注的那部分,从框架层面去把握整体,抓准各个模块的核心,各个模块之间如何耦合,如何同步,如何通信等,这才是能够让你快速进步的最优路线。 4 | 5 | ## 参考 6 | [epoll源码分析](https://icoty.github.io/2019/06/03/epoll-source/) 7 | -------------------------------------------------------------------------------- /OSFoundation/programming_tips.md: -------------------------------------------------------------------------------- 1 | ## 编程小记 2 | 3 | ### 声明中的后缀 “_t” 4 | C语言允许用户使用 `typedef` 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型名称、数组类型名称、指针类型名称与用户自定义的结构型名称、共用型名称、枚举型名称等。一旦用户在程序中定义了自己的数据类型名称,就可以在该程序中用自己的数据类型名称来定义变量的类型、数组的类型、指针变量的类型与函数的类型等. 5 | 6 | 之所以这样做,是因为语言是讲究语境的,通过 `typedef` 给一个固有类型定义一个更符合当前语境的名字,使人在阅读时更容易理解作者的意图。而在进行定义时为了表明这是一个固有类型的“别名”,通常会在名字后面加上一个“_t”,以此表明它是 `typedef` 得来的,从而跟真正的自定义类型区分开。 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | 3 | 分享一些原创和翻译的文章,意在帮助自己熟悉英文,提高英文阅读能力,同时也增加与同行前辈的交流,促进技术的提升,拓展自己的视野。 4 | 5 | *受限于译者英语、技术知识水平及翻译经验,译文难免有词不达意,甚至错误的地方,还望不吝赐教,予以指正* 6 | 7 | 为了更好的分享,接受更多的批评和指正,现已开始支持 Github Pages,欢迎访问[dracarys.github.io](https://dracarys.github.io) 8 | 9 | 10 | ## 许可协议 11 | 12 | 该仓库文章如无特别说明,均采用[知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh)。 13 | 如有译文的原文许可协议与该协议抵触,请以原文许可协议为准。 14 | -------------------------------------------------------------------------------- /Compiling/C&C++ tips.md: -------------------------------------------------------------------------------- 1 | # C&C++ tips 2 | 3 | ## __BEGIN_DECLS 和 __END_DECLS 4 | 这一对宏配合使用,其意义在于与C++混编时指明被宏所包裹的代码是C代码。宏定义如下: 5 | ```c 6 | /* C++ needs to know that types and declarations are C, not C++. */ 7 | #ifdef __cplusplus 8 | # define __BEGIN_DECLS extern "C" { 9 | # define __END_DECLS } 10 | #else 11 | # define __BEGIN_DECLS 12 | # define __END_DECLS 13 | #endif 14 | ``` 15 | 16 | ## Rang for staement 17 | 范围 for 语句。又掉这个坑里了,还是记录一下。 18 | 19 | ```c++ 20 | for (declaration : expression) 21 | statement 22 | ``` 23 | 24 | expression 表示的必须是一个序列(实际上是能返回迭代器的 begin 和 end 成员)。declaration 则定义了一个变量,序列中的每个元素都得能转换成该变量的类型。既然涉及到转换,那么这里究竟是采用的那种转换方式呢 ——— 拷贝构造。所以 statement 中做的修改是不会影响到原有序列中的元素的。那么如果么有要修改的需要怎么办呢 ———— 引用。 25 | 26 | -------------------------------------------------------------------------------- /Compiling/Traditional headermap style is no longer supported.md: -------------------------------------------------------------------------------- 1 | ### Traditional headermap style is no longer supported 2 | 3 | 在用新的 Xcode 打开旧工程时经常会遇到这个问题,解决办法就是:将相应 Target 的 build setting 设置中 “Always Search User Paths” 选项设置为 “NO”,这样问题就解决了,可以这是为什么呢? 4 | 5 | 该设置已经于 Xcode 8.3 开始被废弃,强烈建议将其设置为“NO”。 6 | 7 | 如果开启此选项,无论 `#include ` 还是 `#include "header.h"`,两种形式的引用都会导致预处理优先在 “User Header Search Paths (USER_HEADER_SEARCH_PATHS)” 中检索相应头文件,而早于“Header Search Paths (HEADER_SEARCH_PATHS)”。如此会产生一个问题:假设工程中包含一个我们自己创建的 `String.h` ,此时我们又通过 `#include ` 的方式去引用系统的头文件,那么就会导致始终引用的是我们自己的头文件,而不是系统的。过去的解决办法是在相应的“User Header Search Path”引用项后添加一个 “-iquote” flag 标识,现在则不需要了。 8 | 9 | 弃用“Always search user paths” 后,如果要引用系统或者 Framework 形式的头文件,就用 `#include ` 的形式,找不到,可以将相应的路径添加到 “Header Search Paths” 中。要引用我们自己的创建的,或者是已引入工程的第三方源码头文件,请用 `#include "header.h"` 的形式,如果找不到,那么可以将相应的路径添加到 “User Header Search Paths” 中,这样两个路径独立,且各自独立搜索,就不会再出现“覆盖”的情况。 -------------------------------------------------------------------------------- /iOS/Work_Flow_Of_Hotspot_Helper.md: -------------------------------------------------------------------------------- 1 | 【原】Work Flow Of Hotspot Helper 2 | 3 | 苹果在iOS 9.0 开始在NetworkExtension框架中开放与WiFi热点相关的API,就是本文要讨论的NEHotspotHelper。 4 | 5 | NEHotspotHelper给予App参与Wi-Fi/热点连接的处理过程的机会,诸如此类的应用我们称之为‘热点助手’,系统允许热点助手通过接受命令来参与到连接热点网络的处理事务中。 6 | 7 | 当开始尝试连接一个热点网络时,系机(网络状态及)首先开始进行评估。在这个阶段,所有注册的热点助手都会受到一条要求评估该热点的命令。如果有一个以上的热点助手向系统表示自己可以处理该连接事务,那么系统会选择信任级最高的一个,随之进入鉴权阶段。为优化效率,第一个信任级最高的热点助手将被接纳,其余将被忽略掉。(类似受精的过程) 8 | 9 | 如果没有热点助手可以处理该来接事务,那么系统将直接进入鉴权过程。 10 | 11 | 在鉴权阶段,被系统接纳的热点助手将受到一条要求鉴权的命令。热点助手负责执行一切网络所需的必要处理,以保证网络连接成功和运行。如果通过,那么热点助手将得到一条成功信息,此时系统将进入已鉴权阶段。 12 | 13 | 对于那些已经经过热点助手鉴权的热点网络,一旦再次连接,系统将直接进入维持状态,并且在系统与该网络连接期间,持续处于该状态。热点助手在该阶段,将负责处理一切用以维持连接该网络的事务,只要网络通,热点助手就会返回成功,一旦返回失败,或者返回需要鉴权,那么系统将再次进入鉴权阶段。 14 | 15 | 在鉴权阶段,如果热点助手侦测到需要用户参与,那么热点助手首先以本地通知的方式提示用户,之后返回一个UIRequired的结果,系统将进入进入交互状态。 16 | 17 | 在交互状态,热点助手将受到一条交互状态的命令,该阶段热点助手将不会继续在后台工作,交互方式取决于用户是通过通知或还是直接唤起应用。一旦热点助手得从用户那里得到必要信息,将返回成功随即进入已鉴权状态。 18 | 19 | 除之前介绍的命令,热点助手任何时候都有可能受到一个列表,给予响应数据,热点助手需要标示出列表中的哪些网络可以处理。 20 | -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_database.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之数据库 2 | 3 | ## 1. 数据库基础 4 | ### 1.1 索引的作用、优缺点,与主键的区别 5 | 6 | ### 1.2 乐观锁 VS 悲观锁 7 | 8 | ## 2. SQLite 9 | 10 | [深入理解SQLite](https://www.kancloud.cn/kangdandan/sqlite/64326) 11 | 12 | ### SQLite中插入特殊字符的方法和接受的处理方法? 13 | 14 | ``` 15 | public static String sqliteEscape(String keyWord){ 16 |     keyWord = keyWord.replace("/", "//"); 17 |     keyWord = keyWord.replace("'", "''"); 18 |     keyWord = keyWord.replace("[", "/["); 19 |     keyWord = keyWord.replace("]", "/]"); 20 |     keyWord = keyWord.replace("%", "/%"); 21 |     keyWord = keyWord.replace("&","/&"); 22 |     keyWord = keyWord.replace("_", "/_"); 23 |     keyWord = keyWord.replace("(", "/("); 24 |     keyWord = keyWord.replace(")", "/)"); 25 |     return keyWord; 26 | } 27 | ``` 28 | ### SQLite 与 MySQL 区别 29 | 30 | ## 3. CoreData 31 | 32 | ### CoreData的原理,与SQLite相比优劣? 33 | 34 | ### CoreData的6个成员类 35 | 36 | ### CoreData 多线程中处理大量数据同步时的操作? 37 | 38 | ### NSpersistentStoreCoordinator,NSManagedObjectContext 和 NSManagedObject 中的哪些需要在线程中创建或者传递?你用过什么样的策略实现的? 39 | 40 | 41 | ## 4. MySQL 42 | 43 | 44 | 45 | 46 | 47 | 48 | ## 5. 项目经验 49 | ### 如果不用数据库,只使用普通文件,如何设计亿量级别的日志系统? 50 | 51 | ### 数据库设计、字段设计等项目方案 -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_memory_management.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之 - iOS内存管理 2 | 3 | ### 引用计数是如何实现的 4 | auto reference counting, 5 | 6 | ### 有哪些导致崩溃的常见问题?如何进行预防? 7 | 8 | - 取空值,自定义取值方法,添加到 category 中 9 | - 越界,取值时始终进行验证,且永远不要相信后台反馈 10 | - 不能识别到方法,向错误类型发送消息,预防方法同上,此外还可以通过runtime处理消息转发方法,来减少崩溃 11 | 12 | ### 内存泄漏的原因有哪些?如何解决? 13 | 14 | - 创建后未释放,通过正确释放解决,重点关注 new、create等方法名 15 | - 单位时间开辟大量空间,添加 autoreleasepool 16 | - Corefoundation方法使用不当,持有权转移错误 17 | - 循环引用,添加 weak 中间代理 18 | 19 | ### 常用集合类的哪些操作是深拷贝? 20 | 在集合类对象中,对 immutable 对象进行 copy,是指针复制,mutableCopy 是内容复制;对mutable 对象进行 copy 和 mutableCopy 都是内容复制。但是,集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下: 21 | 22 | ```objc 23 | [immutableObject copy] // 浅复制 24 | [immutableObject mutableCopy] //单层深复制 25 | [mutableObject copy] //单层深复制 26 | [mutableObject mutableCopy] //单层深复制 27 | ``` 28 | ### autorealse 如何实现的 29 | 30 | 这个解释不完善,有待进一步研究 31 | autoreleasePool 是一个Objective-C的一种内存自动回收机制,它可以延时家如autoreleasePool中的变量释放的时机。 32 | autoreleasePool在Runloop时间开始之前(push),释放是在一个 RunLoop 事件即将结束之前(pop) 33 | 34 | ### 子线程中需要加 autoreleasepool 吗?什么时间释放? 35 | 每个线程都默认有一个autoreleasepool,在线程即将退出前释放。但是如果该线程会产生大量的内存碎片,那么建议创建runloop以及自己的释放池,以便可以及时释放。 36 | 37 | ### autoreleasepool 在 ARC 和 MRC 下有什么区别 38 | 39 | ### block 在 ARC 和 MRC 下有什么区别,使用注意事项 40 | 41 | #### BAD_ACCESS 在什么情况下出现?如何调试? 42 | 43 | 读取了一个不是你管控的内存地址,通常是读取了一个已释放或者为初始化的指针 44 | 45 | 启动僵尸对象,然后结合发生错误时的操作,逐步定位问题根源。 -------------------------------------------------------------------------------- /macOS/update_homebrew_remote_source.md: -------------------------------------------------------------------------------- 1 | # 替换 Homebrew 源 2 | 因为中所周知的原因,大陆访问 github 比较缓慢,因此需要将源替换为国内镜像。个人比较推荐中科大的镜像。具体操作如下: 3 | 4 | ```Shell 5 | # 替换brew.git: 6 | $ cd "$(brew --repo)" 7 | # 中国科大: 8 | $ git remote set-url origin https://mirrors.ustc.edu.cn/brew.git 9 | # 清华大学: 10 | $ git remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git 11 | 12 | # 替换homebrew-core.git: 13 | $ cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core" 14 | # 中国科大: 15 | $ git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git 16 | # 清华大学: 17 | $ git remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git 18 | 19 | # 替换homebrew-bottles: 20 | # 中国科大: 21 | $ echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bash_profile 22 | $ source ~/.bash_profile 23 | # 清华大学: 24 | $ echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles' >> ~/.bash_profile 25 | $ source ~/.bash_profile 26 | 27 | # 应用生效: 28 | $ brew update 29 | ``` 30 | 替换过之后,那么如何恢复呢?操作如下: 31 | 32 | ```Shell 33 | # 诊断Homebrew的问题: 34 | $ brew doctor 35 | 36 | # 重置brew.git设置: 37 | $ cd "$(brew --repo)" 38 | $ git fetch 39 | $ git reset --hard origin/master 40 | 41 | # homebrew-core.git同理: 42 | $ cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core" 43 | $ git fetch 44 | $ git reset --hard origin/master 45 | 46 | # 应用生效: 47 | $ brew update 48 | ``` 49 | 50 | > 方法引用自:[简书](https://www.jianshu.com/p/b26c7bc14440) -------------------------------------------------------------------------------- /Ethereum/Structure_of_Ethereum_Keystore_file.md: -------------------------------------------------------------------------------- 1 | # 详解以太坊 Keystore 文件 2 | 3 | ## 文件结构 4 | 5 | 下面是以太坊 `1.7.3` 稳定版客户端 Geth 在私有链上生成的 Keystore 文件内容(与公链文件结构相同): 6 | 7 | ``` JSON 8 | { 9 | "address": "74aadcda1c35fb5a4461eba5cbe2ffaff37a044c", 10 | "crypto": { 11 | "cipher": "aes-128-ctr", 12 | "ciphertext": "9e32071c06f766798ba5617808397e7f6e6f8a3b463dd126eef5e850a7d06bd3", 13 | "cipherparams": { 14 | "iv": "23f0e5e9b559805169ccfa23368f530a" 15 | }, 16 | "kdf": "scrypt", 17 | "kdfparams": { 18 | "dklen": 32, 19 | "n": 262144, 20 | "p": 1, 21 | "r": 8, 22 | "salt": "0476c69012ee4a84af322fe84af9deff931a4ed1d1b275026224112829f49a08" 23 | }, 24 | "mac": "b2b999956c831f81484e79554427e6d2b5351af1f63435863588002c9920e13e" 25 | }, 26 | "id": "6f9b3263-448c-48de-b1ab-951255eaba45", 27 | "version": 3 28 | } 29 | ``` 30 | 31 | 一个包涵了许多参数的 JSON 文件,分为一下四部分: 32 | 33 | - 地址(address); 34 | - 加密信息(crypto); 35 | - id; 36 | - 版本(version); 37 | 38 | ## 加密信息(crypto) 39 | 40 | Keystore文件中的关键信息都存储在该部分,解析来将对各个Key进行详细介绍。 41 | 42 | ### cipher 43 | 44 | 一种对称加密的AES算法,这里用的是 aes-128-ctr 加密模式 45 | 46 | ### ciphertext 47 | 通过 cipher 加密算法对密钥加密后得到的文本,即密文。 48 | 49 | ### cipherparams 50 | cipher 算法(aes-128-ctr)加密时所需的参数,这里只有一个参数:iv 51 | 52 | ### kdf 53 | 密钥生成函数,这里使用的是`scrypt`算法。 54 | 55 | ### kdfparams 56 | scrypt算法需要用到的参数,简单来说就是 dklen、n、p、r、salt都是`kdf`函数需要的参数。有关scrypt函数的详细介绍请参见[这里](https://tools.ietf.org/html/rfc7914) 57 | 58 | ### mac 59 | 用于对我们设置的密码进行校验。详细介绍请参见[这里](https://github.com/hashcat/hashcat/issues/1228) -------------------------------------------------------------------------------- /Non-atomic_vs_atomic.md: -------------------------------------------------------------------------------- 1 | # (未完成)由 nonatomic 和 atomic 引申开去 2 | 3 | iOS 有一道很基础的面试就是 “nonatomic 和 atomic 有什么区别?”,如果回答:一个非原子操作,另一个是原子操作。那么会进一步问:“atomic 的原子操作是线程安全的吗?”,相信大多数人都知道答案——不是,但是随着这道面试题被引用的次数越来越多,传播的越来越广,答案逐渐被简化成了“不安全”,这就偏离了出题人的考察初衷。 4 | 5 | ## 语义同步 6 | 在开始之前,我们先同步几个概念:可预料、不可预料、可确定、不可确定。 7 | 8 | - 不可预料:可能是任何结果。 9 | - 可预料:即结果我们的料想范围之内。例如,可能是 1 ~ 2000 之间的一个值,那么这里我们把它称为可预料的,因为它位于一个范围或者某个类型内。 10 | - 不可确定:仅可预料,但无法确认它的具体值。 11 | - 可确定:结果唯一。例如:结果是 520 这个值,而不是一个范围,或其它任何结果,那么我们称之为可确定的; 12 | 13 | 14 | ## 什么是原子操作 15 | 16 | 要弄清楚这个问题,首先明白什么是原子操作,原子操作是指“不可中断的一个或者一系列操作,也就是不会因线程调度而打断,运行期间不会有任何上下文的切换(context switch)。 17 | 18 | ## 什么是线程安全 这个是自己的理解,但是也需要进一步验证。 19 | 20 | > 之里理解错误,这是原子性 21 | 22 | 所谓线程安全就是:当有多个线程访问同一块内存,不会出现数据错乱,结果应始终是可预料的,或者在可预料的范围内。例如:假设有内存,其存储的是字符串 “hello”,A、B 两个线程同时访问,A 要读取,B 要把 “hello” 更新为字符串 “hi”。如果无论 A、B 操作的顺序如何,只要 A 在读取完之前,B 不可写入,或者 B 写入之前,A 不可读,那么这就是线程安全的;相反,如果不是线程安全的,B 只写了一半,就被 A 读取了,那么此时 A 读取的值就是不可预料的,因为它可能既不是 “hello”,也不是 “hi”。 23 | 24 | 试想一下,线程安全其实解决的是记过是否正确的问题,只有解决这个问题,才进一步考虑结果是否是我们想要的结果。 25 | 26 | ## 单核处理器和多核处理器的区别 27 | 28 | 早期的 CPU 只有一个核心,同一时间内只有一个线程在运行,CPU 必须在各个线程间切换,以实现多任务。此时一条指令中完成的操作即可看作是原子操作,因为中断只发生在指令间。而到了多核 CPU 时代,CPU 可以同时运行 2 个甚至以上的核心,也就是说可以同时运行多个线程,实现了真正的并行,此时再执行类似 `i++` 或 `--i` 这样的操作就可能因为多个线程同时操作同一块内存从而出现无法预料的结果。 29 | 30 | 软件级别的原子操作是依赖于硬件支持的. 在 x86 体系中, CPU 提供了 HLOCK pin 引线, 允许 CPU 在执行某一个指令(仅仅是一个指令)时拉低 HLOCK pin 引线的电位, 直到这个指令执行完毕才放开. 从而锁住了总线, 如此在同一总线的CPU就暂时无法通过总线访问内存了, 这样就保证了多核处理器的原子性. (想想这机制对性能影响挺大的) 31 | 32 | ## atomic 属性 33 | 34 | atomic 修饰符用来向编译器表明:在生成指令时要保证同一时间内只有一个线程在访问。虽然数据不会发生错乱了,但是因为顺序没有保证,所以最终的结果仍然可能不是我们想要的。 35 | 36 | ???这里感觉不对,还有待进一步考证。 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ## 参考 48 | [单核,多核 CPU 的原子操作](https://www.cnblogs.com/javaleon/p/4292656.html) 49 | 50 | [关于 atomic 为何不安全](http://www.cocoachina.com/bbs/read.php?tid-1720812-page-1.html) 51 | 52 | [Intel support forums](https://software.intel.com/en-us/forums/intel-moderncode-for-parallel-architectures/topic/305924) 53 | 54 | [Atomic vs. Non Atomic Properties Crash Course](https://medium.com/@YogevSitton/atomic-vs-non-atomic-properties-crash-course-d11c23f4366c) -------------------------------------------------------------------------------- /Linux/Ubuntu_tips.md: -------------------------------------------------------------------------------- 1 | # 【整理】Ubuntu 学习笔记 2 | 3 | Ubuntu 也断断续续用了不短时间,虚拟机的常驻系统之一,但是上周末在家里远程部署一个 Flask 小项目时竟然把一些命令又忘了,,为了改善这种现用现忘现查的情况,也为了以后查询更方便😁,特此整理。 4 | 5 | ## apt 安装包时提示找不到 6 | 7 | 这很可能是因为你首次使用 apt,还没有对其更新造成的。通过以下命令更新即可: 8 | 9 | ```shell 10 | apt update 11 | ``` 12 | 13 | > 注意,很多包都需要 root 权限,注意切换用户。 14 | 15 | 16 | ## Unable to install "package-name": snap "package-name" has "install-snap" change in progress 17 | 通过 snap store 安装应用,如果尚未安装完你就去查看了其他应用,之后再次返回时安装进度就很可能会丢失,此时如果你再次选择 “install”,就会得到该错误。这是因为安装进程仍在进行中,至少是它认为仍在进行中。 18 | 19 | 怎么解决呢?在万能的终端中输入如下命令,查看正在进行的安装: 20 | 21 | ```shell 22 | $ snap changes 23 | 24 | ID Status Spawn Ready Summary 25 | 1 Doing today at 11:09 CST - Install "AppName" snap 26 | ``` 27 | 28 | 应该能在结果中看到,你想装而装不了的应用,状态为 `Doing`。这就是问题的所在了,接下我来我们只要在终端中终止它就可以了。 29 | 30 | ```shell 31 | sudo snap abort 1 32 | ``` 33 | 34 | 注意命令中 `abort` 的是 id,要与上一条命令的结果中的条目 id 相同。 35 | 36 | > 如果你跟我一样,对 Snap 不甚了解,那么可以参见这篇文章:[《Ubuntu 18.04及Snap体验》](https://www.linuxidc.com/Linux/2018-06/152993.htm) 37 | 38 | 39 | ## linux shell 中的 “>/dev/null 2>&1” 40 | 41 | /dev/null 表示一个 linux 上的空设备文件,所有写入这个文件的内容都会丢失,俗称”黑洞“,个人习惯称之为”下水道“,更符合自己对(水)流的认知。 42 | 43 | shell 中每个程序运行后都会至少打开3个文件描述符: 44 | 45 | |类型|文件描述符|默认情况|对应文件句柄位置| 46 | |:--|:--|:--|:--| 47 | |标准输入(standard input)|0|从键盘获得输入|/proc/self/fd/0| 48 | |标准输出(standard output)| 1| 输出到屏幕(即控制台)| /proc/self/fd/1| 49 | |错误输出(error output)|2|输出到屏幕(即控制台)|/proc/self/fd/2| 50 | 51 | 52 | “2>&1” 中的 2 和 1 即分别表示标准的错误流和输出流。 53 | 54 | 既然有了流,那么自然就有像控制水管一样的流向控制需求,为了满足这一需求,引入了 `>` 符号。该符号表示将流进行重定向,尖角指向即表示流的流动方向。具体用法如下: 55 | 56 | |命令|介绍| 57 | |:--|:--| 58 | |command >filename|把标准输出重定向到新文件中| 59 | |command 1>filename|同上| 60 | |command >>filename|把标准输出追加到文件中| 61 | |command 1>>filename|同上| 62 | |command 2>filename|把标准错误重定向到新文件中| 63 | |command 2>>filename|把标准错误追加到新文件中| 64 | 65 | 既然如此,那么直接`2>1`不就好了吗,为什么还要多一个 `&` 符号?这是为了和标准的文件进行区分,如果没有,shell 会把它解释为向一个名称为 ”1“ 的文件流出。而要表示标准输出就必须用`&` 符号进行区分。 66 | 67 | 参考1:[《如何理解Linux shell中的“2>&1”》](https://zhuanlan.zhihu.com/p/47765176) 68 | 69 | 参考2:[《linux下详解shell中>/dev/null 2>&1》](https://www.cnblogs.com/ultranms/p/9353157.html) -------------------------------------------------------------------------------- /iOS/Tool_tips.md: -------------------------------------------------------------------------------- 1 | # 【整理】Tool tips 2 | 3 | ## 1、显示Info.plist原始键/值 4 | 5 | 默认情况下,为了便于阅读Info.plist中显示的键/值并不是真实的键/值名称。但如果想查看 actual key 怎么办呢? 6 | 在Info.plist的任意项目,点击鼠标右键->Show Raw keys/values 7 | 8 | ## 2、pod install vs. pod update 9 | 10 | 很多人都这样认为,第一次配置时用 `pod install`,之后就全用 `pod update`,还有一些博客说从 github 上 clone 下来的项目全用 `pod update`,这些都不完全对。 11 | 12 | ### pod install 13 | 14 | 初次运行 `pod install` 命令,除了会解析依赖,下载依赖外,还会生成一个 `Podfile.lock` 文件,对所有的依赖进行版本追踪。如果之后再次运行 `pod install` 命令,将会检查并下载安装 `Podfile.lock` 文件中记录的版本,跳过解析的过程,除非某依赖未包含在 `Podfile.lock` 中。所以为了保持依赖的一致性,务必将 `Podfile.lock` 文件纳入版本控制中。 15 | 16 | ### pod update 17 | 18 | 运行 `pod update PODNAME` 命令,则会直接查询较新的依赖版本,而不会检查 `Podfile.lock`文件,如果未指定版本,则会下载最新的可用版本。如果未指定具体的 pod,则会更新所有依赖。 19 | 20 | 由此可见,Cocoapods 即保证了多人下发版本依赖的一致性,又兼顾了个人的更新需求。 21 | [参见](https://guides.cocoapods.org/using/pod-install-vs-update.html) 22 | 23 | ## 3、安装 Network Link Conditioner 24 | 该工具现在包含在 Aditional Tool For Xcode 包中,需登陆开发者网站下载。详情不多讲,这里主要介绍一下安装。 25 | 26 | 在 macOS Mojava 10.14.4 目前最新的系统上,直接双击是无法安装的,会提示你 “Network Link Conditioner 是跟随系统的,无法被替换”,什么鬼?系统明明没有自带,却告诉我无法安装?直接手动磕,打开目录 `~/Library/PreferencePanes`,直接将 Conditioner 拖进去,重启。Done! 27 | 28 | > [参考来源](https://stackoverflow.com/questions/52414375/cannot-install-xcode-10-network-link-conditioner-in-macos-mojave) 29 | 30 | ## 4、App 冷启动过程分析 31 | WWDC 曾有一个专题讲过这个问题,这里只说明如何开启环境变量,统计启动过程中各阶段的耗时。首先选择对应的工程 Target,然后 Edit Scheme,选择 Run,这时就可以看到环境变量设置了。在环境变量中添加: 32 | 33 | ```shell 34 | DYLD_PRINT_STATISTICS = 1 // 注意 Xcode 里是键值模式,这里仅是举例。 35 | ``` 36 | 如此,再次运行及可以统计到各个启动阶段的耗时: 37 | 38 | ```shell 39 | Total pre-main time: 545.91 milliseconds (100.0%) 40 | 41 | dylib loading time: 184.95 milliseconds (33.8%) 42 | 43 | rebase/binding time: 34.31 milliseconds (6.2%) 44 | 45 | ObjC setup time: 99.27 milliseconds (18.1%) 46 | 47 | initializer time: 227.28 milliseconds (41.6%) 48 | 49 | slowest intializers : 50 | 51 | libSystem.B.dylib : 6.39 milliseconds (1.1%) 52 | 53 | XxxxXxXxxxV3 : 390.00 milliseconds (71.4%) 54 | ``` 55 | 从上面的例子可以看到,71.4% 的时间都耗费在应用上了,其实这对后继者来说是好事,因为可以优化的空间很大。 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /OSFoundation/About_locks_II.md: -------------------------------------------------------------------------------- 1 | # 【笔记】关于锁的选择 2 | 3 | 上一篇介绍了各种锁,并简单阐述了各种锁的特点及其实现。那么在实际开发的过程中,如何选择恰当的锁呢? 4 | 5 | ## 选择标准 6 | 总结起来,选择标准就两点: 7 | 8 | 1. 不能造成死锁 9 | 2. 在第一点的前提下,追求高效率 10 | 11 | ## 避免死锁 12 | 造成死锁的四个条件,相比大家都耳熟能详: 13 | 14 | 1. 条件互斥 15 | 2. 占有和等待条件 16 | 3. 不可抢占条件 17 | 4. 环路等待条件 18 | 19 | 为了避免死锁,通常会用到:**层次锁(hierarchical locking)** ,在多个相关联的锁上规定一个顺序,要求所有的线程都以相同的顺序加锁,以此来避免死锁的发生。 20 | 21 | 但是在有些场景下,我们必须违反加锁顺序。考虑以下场景,有一个以双向链表实现的 LRU 缓存。它通过自旋锁来保护队列头部和队列中缓冲对象的前后向指针。每个缓冲对象还存在一个自旋锁用来保护对象自身信息。当访问这个对象时,这个锁必须被持有。 22 | 23 | 当某个线程想要访问某个特定的缓冲对象时,显示通过哈希表(也可能是其它方式)定位到对象,然后加锁。如果要进一步从缓存列表中删除该对象,还需对链表加锁。所以加锁的顺序是先缓存对象,后缓存列表。 24 | 25 | 然而有时候,线程需要一个空缓存对象,然后在对其进行更新,那么此时就要先锁住缓存列表,然后在锁住列表上的首个缓存对象。此时的顺序就变成了先锁列表,在锁缓存对象。 26 | 27 | 如果此时有两个线程,分别以这两种顺序进行加锁,那么就可能出现双方都锁住了部分资源,又都等待对方释放自己所需资源的情况,从而导致死锁。 28 | 29 | 为了避免该情况发生,此时就应该使用:**随机锁(stochastic locking)**,当某个线程尝试获取一个可能会破坏层次的锁时,可以使用 `try_lock()` 来代替 `lock()` 操作,尝试获取锁,如果锁已经被持有,它会返回失败,而不是阻塞在这个锁上。在上面的例子中,尝试获取一个空缓存对象的线程会先锁住列表,然后沿着列表,逐个尝试 `try_lock()`,直至找到一个可以锁住的缓冲区。 30 | 31 | 一个自旋锁的 `try_lock()` 实现: 32 | 33 | ```C 34 | int try_lock(spinlock *s) { 35 | if(test_and_set(s) != 0) 36 | return false; 37 | else 38 | return true; 39 | } 40 | ``` 41 | 42 | ## 递归锁 43 | 当一个线程需要获取自己已经拥有的锁时,那么我们就必须使用递归锁,否则将造成死锁。 44 | 45 | ### 阻塞还是自旋 46 | 大多数复杂的锁都可以被实现为阻塞锁,或者复杂的自旋锁,而不影响其功能。此时选择阻塞还是自旋就要从性能上进行考量了。 47 | 48 | 因为忙等会占用处理器,一般不怎么受待见。但是,一些特殊场景却必须使用忙等。例如,线程已经有一个简单的互斥锁,那么它就不能被阻塞,因为它还要负责唤醒被它所持有的互斥锁阻塞的线程。此时如果它尝试获取另一个简单的互斥锁,它将进入忙等;如果它试图获取一个复杂锁,那么它会释放它已经持有的互斥锁。 49 | 50 | 但是我们还要考虑到一个问题,就是因为阻塞而带来的睡眠和唤醒是开销很大的操作,它包括了每次结束时的上下文切换,以及睡眠队列和调度器队列的操作。所以对那些很快就可用的资源使用阻塞锁显然是不够效率的。 51 | 52 | 此外,一些资源是被短时间占用,还是长时间持有,是需要视具体情况。例如,当我们操作内存缓存的时候,很快即可操作完毕;但是当从内存向磁盘同步时,就可能需要较长的时间。面对这种情况时,如果单独使用自旋或者阻塞,都不能算是一个很好的方案。一个可选的方法是提供两种锁。 53 | 54 | ### 锁对象 55 | 56 | 57 | ### 粒度和持续时间 58 | 59 | 此外还需要考虑锁的持续时间。持有锁的时间越短越好,这样减少能极少锁上的竞争。然而,有时候,这可能导致额外的加锁和解锁。例如,某个线程需要在一个对象上执行两个操作,这两个操作都需要锁住对象。而两个操作之间,线程还需要执行一些额外不相关的任务。那么它就可能在第一个操作之后对该对象解锁,然后在第二次操作时再加锁。实际上,如果这个不相关的任务非常短暂,保持一直加锁的状态可能会更好,可以省去一次加解锁的开销。 60 | 61 | ### 最后 62 | 本人一直从事客户端开发,涉及到锁,尤其是复杂锁的应用场景接触的并不多。工作中遇到的一些问题,几乎都可以通过 Google 来解决。但是慢慢发现一个问题,大家都在介绍锁,怎么用锁,但很少有文章设计如何选择锁。恰好最近在翻阅《深入理解 UNIX 系统内核》这本书,算是解决了心中的部分疑惑,遂总结了这篇笔记。 63 | 64 | 65 | ### 参考 66 | 67 | - 《深入理解 UNIX 系统内核》 Uresh Vahalia 著, 李雨、薛磊、黄庆新 译 68 | -------------------------------------------------------------------------------- /Python/Flask/Flask_notes_02.md: -------------------------------------------------------------------------------- 1 | # 【整理】Flask 学习笔记之二 2 | 3 | Unreal Engine 里也有一个 Blueprint 的概念。不知道有什么联系,UE 研究的还不够深,这里咱不展开,留待日后补习。 4 | 5 | ### 蓝图(blueprint) 6 | 7 | 为了在一个或多个应用中,使应用模块化并且支持常用方案,Flask 引入了蓝图概念。蓝图可以极大地简化大型应用并为了扩展提供集中地注册入口。**Blueprint**对象与**Flask**应用对象的工作方式类似,但不是一个真正的应用,它为 Flask 描述了该如何构建和扩展应用。 8 | 9 | ### 为什么应用蓝图 10 | 11 | 蓝图的用途: 12 | 13 | - 把一个应用分解为一套蓝图。针对大型应用的理想方案:一个项目可以实例化一个应用,初始化多个扩展,并注册需多个蓝图; 14 | - 在一个应用的 URL 前缀和/或子域上注册一个蓝图。URL 前缀和/或子域的参数成为蓝图中所有视图的通用试图参数(默认情况下); 15 | - 使用不同的 URL 规则在应用中多次注册蓝图; 16 | - 通过蓝图提供模板过滤器、静态文件、模板和其它工具。蓝图不必执行应用或视图函数; 17 | - 当初始化一个 Flask 扩展是,为以上任意一种用户注册一个蓝图 18 | 19 | Flask 中的蓝图不是一个可插拔的应用,因为它不是一个真正的应用,而是一套可以注册在应用中的操作,并且可以注册多次。为什么不采用多个应用对象?多应用会导致每个应用都有自己独立的配置,且只能在 WSGI 层中管理应用。而使用蓝图,应用会在 Flask 层中进行管理,共享配置,通过注册按需改变应用对象。 20 | 21 | 蓝图的缺点 ,除非应用被销毁,否则不能注销蓝图。 22 | 23 | 感觉 Flask 应用像是一个“经理”,手底下管着一帮“小弟”--蓝图,每个小弟各司其职。 24 | 25 | ### 创建蓝图 26 | 27 | 代码如下: 28 | 29 | ``` Python 30 | from flask import Blueprint, render_template, abort 31 | from jinja2 import TemplateNotFound 32 | 33 | # 声明蓝图 34 | simple_page = Blueprint('simple_page', __name__, 35 | template_folder='templates') 36 | 37 | # 关联蓝图 38 | @simple_page.route('/', defaults={'page': 'index'}) 39 | @simple_page.route('/') 40 | def show(page): 41 | try: 42 | return render_template('pages/%s.html' % page) 43 | except TemplateNotFound: 44 | abort(404) 45 | ``` 46 | 47 | 通过 `` 函数声明一个蓝图,并将其与相应的 URL 关联起来。 48 | 49 | ### 注册蓝图 50 | 51 | 代码如下: 52 | 53 | ``` Python 54 | from flask import Flask 55 | from yourapplication.simple_page import simple_page 56 | 57 | app = Flask(__name__) 58 | app.register_blueprint(simple_page) 59 | ``` 60 | 注册蓝图后形成的规则: 61 | 62 | ``` Json 63 | [ 64 | ' (HEAD, OPTIONS, GET) -> static>, 65 | ' (HEAD, OPTIONS, GET) -> simple_page.show>, 66 | simple_page.show> 67 | ] 68 | ``` 69 | 第一条是应用本身的用于静态文件。后面两条出自蓝图 simple_page 的 `show()` 函数。 70 | 71 | 除此之外,蓝图还可以挂接到不同的位置: 72 | 73 | ``` Python 74 | app.register_blueprint(simple_page, url_prefix='/pages') 75 | ``` 76 | 如此就形成了新的规则: 77 | 78 | ``` Json 79 | [ 80 | ' (HEAD, OPTIONS, GET) -> static>, 81 | ' (HEAD, OPTIONS, GET) -> simple_page.show>, 82 | simple_page.show> 83 | ] 84 | ``` 85 | 86 | 如果想多次注册蓝图,那么就要考虑蓝图的复用性。 87 | 88 | 89 | -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_Swift.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之 Swift 2 | 3 | ### 什么是 Optional 类型,它用来解决什么问题? 4 | 5 | Swift 是严格类型安全的语言,Optional 表示某中可选类型,即它如果有值那么它就是 x,否则就是没有值,nil。它能避免很多因为意外nil而出现的错误。 6 | 7 | ### Optional 类型是如何实现的? 8 | 9 | ### 什么情况下不得不使用隐式拆包?为什么? 10 | - 对象属性在初始化的时候不能nil,否则不能被初始化。典型的例子是Interface Builder outlet类型的属性,它总是在它的拥有者初始化之后再初始化。在这种特定的情况下,假设它在Interface Builder中被正确的配置——outlet被使用之前,保证它不为nil。 11 | - 解决强引用的循环问题——当两个实例对象相互引用,并且对引用的实例对象的值要求不能为nil时候。在这种情况下,引用的一方可以标记为unowned,另一方使用隐式拆包。 12 | 13 | ### 可选类型解包的方式有哪些?安全性如何? 14 | 15 | ### 什么时候用 Structure,什么时候用 Class 16 | 值类型和引用类型的区别 17 | 18 | ### 什么是泛型?用来解决什么问题的? 19 | 通过泛型可以定义类型安全的数据结构(类型安全),而无需使用具体的数据类型(可扩展)。这能够显著提高性能并得到更高质量的代码(高性能),因为你可以重用数据处理算法,而无须复制类型特定的代码(可重用)。 20 | 21 | ### 闭包是引用类型吗? 22 | 是引用类型,不会发生复制。 23 | 24 | ### 搜索关键词 Swift Interview Questions 25 | 26 | ### what is the type of x? And what is its value? 27 | 28 | ``` Swift 29 | let d = ["john": 23, "james": 24, "vincent": 34, "louis": 29] 30 | let x = d.sorted{ $0.1 < $1.1 }.map{ $0.0 } 31 | ``` 32 | 复习一些 Swift 的容器,尤其是源码,看看他们的实现方式。 33 | 34 | ### what's the differences between `unowned` and `weak`? 35 | 36 | - unowned: the reference is assumed to always have a value during its lifetime - as a consequence, the property must be of non-optional type 37 | - weak: at some point it's possible for the reference to have no value - as a consequence, the property must be of optional type. 38 | 39 | ### Swift 应用了面向协议编程? 40 | 41 | #### Swift 中 Array 是值类型,他有一个 copy on wirte 的行为,具体怎么实现的? 42 | 对于简单值类型 (像是 Int 或 CGPoint) 来说,整个值直接存储在一个变量中,当初始化一个新变量,或是将新值赋给已经存在的变量时,复制都会自动发生。 43 | 44 | 然而,将一个数组赋给新变量并不会发生底层存储的复制,这只会创建一个新的引用,它指向同一块在堆上分配的缓冲区,所以该操作将在常数时间内完成。直到指向共享存储的变量中有一个值被更改了 (例如:进行 insert 操作),这时才会发生真正的复制。不过要注意的是,只有在改变时底层存储是共享的情况下,才会发生复制存储的操作。如果数组对它自身存储所持有的引用是唯一的,那么直接修改存储缓冲区也是安全的。 45 | 46 | 当我们说 Array 实现了写时复制优化时,我们本质上是在对其操作性能进行一系列相关的保证,从而使它们表现得就像上面描述的一样 47 | 48 | ### 函数式编程,什么事“单子(Monad)”、“函子(functors)”? 49 | 50 | 一个“函子”是一种表示 Type 的类型,它: 51 | 52 | - 封装了另一种类型(类似于封装了某个 `T` 类型的 `Array` 或 `Optinal`) 53 | - 有一个具有 `(T->U) -> Type` 签名的 `map` 方法 54 | 55 | 一个“单子”是一种类型,它: 56 | 57 | - 是一个函子(所以它封装了一个 `T` 类型,拥有一个 `map` 方法) 58 | - 还又一个具有 `(T->Type) -> Type`签名的 `flatMap` 方法 59 | 60 | 简单来说:一个单子就是一种带有 flatMap 方法的类型,一个函子就是以中带有一个 61 | 62 | ### Swift和Objective-C混编,各自定义的静态变量(常量)如何排布?为什么? 63 | 在模拟器上测试的结果是Objective-C和Swift是各自独立的区域,Objective-C的静态变量(常量)会处于低区,Swift位于高区,它们之间并不连续。 64 | 65 | 为什么?暂时不清楚,有待进一步探究。 66 | -------------------------------------------------------------------------------- /Solidity/Solidity_in_depth_-_Structure_of_a_Contract.md: -------------------------------------------------------------------------------- 1 | # 【译文】深入学习 Solidity 之合约结构 2 | 3 | Solidity 中合约(Contract)与面向对象语言中的类(Class)非常相似。每个合约都可以包含以下声明,状态变量(State Variables),函数(Function), 函数修饰器(Function Modifiers), 事件(Events), 结构体(Struct Type)和枚举(Enum Types)。不仅如此,合约还可以继承自其它合约。 4 | 5 | ## 状态变量(State Variables) 6 | 7 | 在合约中,状态变量被用来存储一些数值。 8 | 9 | ``` Solidity 10 | pragma solidity ^0.4.0; 11 | 12 | contract SimpleStorage { 13 | uint storedData; // State variable 14 | // ... 15 | } 16 | 17 | ``` 18 | 19 | ## 函数(Functions) 20 | 21 | 函数是合约中的可执行代码单元。 22 | 23 | ``` Solidity 24 | pragma solidity ^0.4.0; 25 | 26 | contract SimpleAuction { 27 | function bid() public payable { // Function 28 | // ... 29 | } 30 | } 31 | ``` 32 | 33 | 函数既可以在定义它的合约被调用,也可以被其它合约调用,相对与内部调用,外部调用可以拥有不同的访问级别(更多请参见 Visibility and Getters)。 34 | 35 | ## 函数修饰器(Function Modifiers) 36 | 37 | 函数修饰器可以通过声明的方式,对某些函数的语义进行修饰(更多请参见 Contract部分的 Function Modifiers ) 38 | 39 | ``` Solidity 40 | pragma solidity ^0.4.11; 41 | 42 | contract Purchase { 43 | address public seller; 44 | 45 | modifier onlySeller() { // Modifier 46 | require(msg.sender == seller); 47 | _; 48 | } 49 | 50 | function abort() public onlySeller { // Modifier usage 51 | // ... 52 | } 53 | } 54 | ``` 55 | 56 | ## Events 57 | 58 | 事件是输出 EVM 日志的一个便利接口。 59 | 60 | ``` Solidity 61 | pragma solidity ^0.4.0; 62 | 63 | contract SimpleAuction { 64 | event HighestBidIncreased(address bidder, uint amount); // Event 65 | 66 | function bid() public payable { 67 | // ... 68 | HighestBidIncreased(msg.sender, msg.value); // Triggering event 69 | } 70 | } 71 | ``` 72 | 73 | 欲了解事件是如何声明的,以及如何在 DApp 中应用,请参见 Contract 一节。 74 | 75 | ## 结构体类型(Struct Types) 76 | 77 | 结构体是自定义数据类型,它可以打包多个变量(请参见 types 部分中的 Structs一节)。 78 | 79 | ``` Solidity 80 | pragma solidity ^0.4.0; 81 | 82 | contract Ballot { 83 | struct Voter { // Struct 84 | uint weight; 85 | bool voted; 86 | address delegate; 87 | uint vote; 88 | } 89 | } 90 | ``` 91 | 92 | ## 枚举类型(Enum Types) 93 | 94 | 枚举可以用来创建包含一组有限值的自定义类型(请参见 types 部分中的 Enums 一节)。 95 | 96 | ``` Solidity 97 | pragma solidity ^0.4.0; 98 | 99 | contract Purchase { 100 | enum State { Created, Locked, Inactive } // Enum 101 | } 102 | 103 | ``` -------------------------------------------------------------------------------- /OSFoundation/Virtual_memory.md: -------------------------------------------------------------------------------- 1 | # 【笔记】虚拟内存 2 | 3 | 该文章是《深入理解计算机系统》中第九章关于 VM 的读书笔记,有大量的原文摘抄,所以本篇文章不在仓库的许可协议之内。如您想了解相关知识请积极购买正版。 4 | 5 | 虚拟内存提供的三种能力: 6 | 7 | - 将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据; 8 | - 为每个进程提供给了一致的地址空间,从而简化内存管理; 9 | - 保护每个进程的地址空间不被其它进程破坏 10 | 11 | ## 9.1 物理和虚拟寻址 12 | 13 | 计算机系统的主存被组织成一个由 M 个连续的子节大小的单元组成的数组。每个字节都有一个唯一的物理地址(Physical Address,PA)。 14 | 15 | 早起直接物理寻址,现代采用虚拟寻址(virtual addressing)。 16 | 17 | 从 Virtual address,VA 到 Pysical addres,PA 的过程被称为 Address translation。 18 | 19 | ## 9.2 地址空间 20 | 21 | 地址空间(Address space)是一个非负整数地址的有序集合。地址空间的大小有表示最大地址所需的位数来描述。32位、64位地址空间。 22 | 23 | 地址空间的概念很重要,因为他清楚地区分了数据对象(子节)和它们的属性(地址)。主存中的每个子节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。 24 | 25 | 虚拟地址空间(Virtual address space)。 26 | 27 | ## 9.3 虚拟内存作为缓存的工具 28 | 29 | 虚拟内存被组织位一个由存放在次盘上的 N 个连续的字节大小的单元组成的数组。每个字节都有一个唯一的虚拟地址,作为到数组的索引。 30 | 31 | 虚拟内存被分为虚拟页(Virtual Page, VP),物理内存被分割为物理页(Physical Pages,PP) 32 | 33 | 任何时刻,虚拟页面的集合都分为三个不相交的子集: 34 | 35 | - 为分配的:VM 系统还为分配的页。未分配的块没有任何数据和它们相关联,因此也就不占用任何磁盘空间。 36 | - 缓存的:当前已缓存在物理内存中的已分配页; 37 | - 为缓存的:为缓存在物理内存中的已分配页。 38 | 39 | ### 9.3.1 DRAM缓存的组织结构 40 | 41 | SRAM 缓存表示位于 CPU 和主存之间的 L1,L2,L3高速缓存 42 | 43 | DRAM 缓存表示虚拟内存系统的缓存,他在主存中缓存虚拟页。 44 | 45 | 虚拟页往往很大,通常是4KB~2MB。 46 | 47 | DRAM 总是回写,不是直写。 48 | 49 | ### 9.3.2 页表 50 | 51 | 为什么需要页表?因为系统需要一种方法来判断一个虚拟页是不是缓存在 DRAM 中了,是,缓存在哪?不是,即不命中,那么这个虚拟页在磁盘的哪个位置?找到后与要牺牲哪个页?页表就是为了解决这些问题而诞生的方案。 52 | 53 | 页表负责将虚拟页映射到物理页。其实就是一个页表条目(Page Table Entry,PTE)的数组。 54 | 55 | ![Page table](./images/page_table.png) 56 | 57 | ### 9.3.3 页命中 58 | 59 | ### 9.3.4 缺页 60 | 61 | DRAM 混存不命中称为缺页(page fault) 62 | 63 | 查询页表->为缓存->触发缺页异常->调用内核缺页异常处理->选择牺牲页->将牺牲页复制回磁盘->将目标页复制到原牺牲页的位置->更新页表->返回重新执行导致缺页的指令 64 | 65 | 交换(swapping)/页面调度(paging):在磁盘和内存之间传送页的活动。 66 | 67 | ### 9.3.5 分配页面 68 | 69 | ### 9.3.6 局部性 70 | 71 | 抖动(thrashing)页面不断地换进换出。 72 | 73 | ## 9.4 虚拟内存作为内存管理的工具 74 | 75 | 操作系统为每个进程提供了一个独立的页表,因而也就是一个独立的虚拟地址空间。 76 | 77 | ## 9.5 虚拟内存作为内存保护的工具 78 | 79 | PTE(Page table entry)上有权限标识。 80 | 81 | 如果一条指令违反了这些许可条件,那么 CPU 就会触发一个一般保护故障,将控制传递给一个内核中的异常处理程序。Linux shell一般将这种异常报告为“段错误(segmentation fault) 82 | 83 | ## 9.6 地址翻译 84 | 85 | 略 86 | 87 | ## 9.7 案例研究:Intel Core i7/Linux 内存系统 88 | 89 | ### 9.7.1 Core i7 地址翻译 90 | 91 | ### 9.7.2 Linux 虚拟内存系统 92 | 93 | ![Virtual memory of Linux 01](./images/virtual_memory_of_linux_01.png) 94 | 95 | ![Virtual memory of Linux 02](./images/virtual_memory_of_linux_02.png) 96 | 97 | ![Page fault of linux](./images/page_fault_of_linux.png) 98 | 99 | ## 9.8 内存映射 100 | 101 | -------------------------------------------------------------------------------- /Java/Java_Anotations.md: -------------------------------------------------------------------------------- 1 | # Java 注解 2 | Java 注解学习笔记,原文:[Java中的注解原来是这样回事的](https://blog.csdn.net/swpu_ocean/article/details/83352128) 3 | 4 | 通俗的理解注解就是类似书页上的注解一样,当你看到一个名词时,对它有一定初步的认识,当进一步了解注解时,那么便会了解一些额外信息。Java 中注解需要我们自己定义(当然有已有的),可以自由的添加一些额外信息,当在对被注解的对象进行应用时,就可以对这些附带的额外信息进行解释(注解处理)。 5 | 6 | ## 内置注解 7 | 8 | - @Override 9 | - @Deprecated 10 | - @SupperessWarnings 11 | 12 | ## 元注解 13 | 14 | 元注解即用来描述注解的注解。按 OO 的思想,假设注解是一个对象,那么谁来定义注解呢,那就是元注解。 15 | 16 | ### @Target 17 | 18 | 一表胜千言 19 | 20 | |参数|说明| 21 | |:-|:-| 22 | |CONSTRUCTOR|构造器的声明| 23 | |FIELD|域声明(包括enum实例)| 24 | |LOCAL_VARIABLE|局部变量声明| 25 | |METHOD|方法声明| 26 | |PACKAGE|包声明| 27 | |PARAMETER|参数声明| 28 | |TYPE|类、接口(包括注解类型)或enum声明| 29 | 30 | ### @Retention 31 | 32 | |参数|说明| 33 | |:-|:-| 34 | |SOURCE|注解将被编译器丢弃| 35 | |CLASS|注解在class文件中可用,但会被JVM丢弃| 36 | |RUNTIME|JVM将在运行期也保留注解,因此可以通过反射机制读取注解的信息| 37 | 38 | ### @Documented 39 | 40 | 41 | ### @Inherited 42 | 43 | 44 | ## 如何定义注解 45 | 46 | ```java 47 | @Target(ElementType.METHOD) 48 | @Retention(RetentionPolicy.RUNTIME) 49 | public @interface Test { 50 | 51 | } 52 | ``` 53 | 54 | 在后续的使用中直接 `@Test` 就可以使用我们自己定义的注解了。但是因为这个注解没有实现任何功能,所它什么也不会做。 55 | 56 | ```java 57 | @Target({ElementType.TYPE}) 58 | @Retention(RetentionPolicy.RUNTIME) 59 | public @interface Table { 60 | String name() default ""; 61 | 62 | String catalog() default ""; 63 | 64 | String schema() default ""; 65 | 66 | UniqueConstraint[] uniqueConstraints() default {}; 67 | 68 | Index[] indexes() default {}; 69 | } 70 | ``` 71 | 72 | ## 注解处理器 73 | 74 | 定义一个简单的注解 75 | 76 | ```java 77 | @Target(ElementType.METHOD) 78 | @Retention(RetentionPolicy.RUNTIME) 79 | public @interface Person{ 80 | String name() default "I don't have name"; 81 | int age() default 21; 82 | } 83 | ``` 84 | 85 | 应用到实体类中: 86 | 87 | ```java 88 | public class MyLove { 89 | 90 | @Person(name = "My name is zhy") 91 | public String zhy(){ 92 | return "zhy"; 93 | } 94 | 95 | @Person(name = "My name is xyx", age = 19) 96 | public String xyx(){ 97 | return "xyx"; 98 | } 99 | 100 | } 101 | ``` 102 | 103 | 相应的注解处理器: 104 | 105 | ```java 106 | import java.lang.reflect.Method; 107 | import java.util.List; 108 | 109 | public class MyLoveTest { 110 | 111 | public static void myLoveTest(List ages, Class cl) { 112 | Method[] methods = cl.getDeclaredMethods(); 113 | for (Method method : 114 | methods) { 115 | Person person = method.getAnnotation(Person.class); 116 | if (person != null) { 117 | System.out.println("My name is " + person.name() + "and I'm " + person.age()); 118 | ages.remove(person.age()); 119 | } 120 | } 121 | 122 | for (int i : 123 | ages) { 124 | System.out.print("Missing age is " + i); 125 | } 126 | } 127 | } 128 | ``` -------------------------------------------------------------------------------- /Compiling/How_to_ cross_compiled_osip2_and_eXosip2_with_Mac.md: -------------------------------------------------------------------------------- 1 | # 在Mac上为iOS交叉编译osip2和eXosip2 2 | 3 | 关于osip2和eXosip2是什么这里不比多说,相信看到找到此片文章的都已十分清楚sip协议栈。本文的重点在与如何在Mac平台上以iOS为目标平台交叉编译osip2和eXosip2。 4 | 5 | 下面以iOS模拟器和iOS真实设备为目标平台,分别列出编译配置: 6 | 7 | ## 编译配置 8 | 9 | ### iOS 模拟器 10 | 11 | - osip2 12 | 13 | ``` 14 | $ ./configure CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang CFLAGS="-O2 -m32 -mios-simulator-version-min=5.0 -DPJ_SDK_NAME="\"iPhoneSimulator7.1.sdk\"" -arch i386 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk" --host=arm-apple-darwin9 --target=arm-apple-darwin9 --prefix=/Users/noone/Documents/siplibs/i386 15 | ``` 16 | - eXosip2 17 | 18 | ``` 19 | $./configure CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang CFLAGS="-O2 -m32 -mios-simulator-version-min=5.0 -DPJ_SDK_NAME="\"iPhoneSimulator7.1.sdk\"" -arch i386 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk" LDFLAGS=" -framework MobileCoreServices -framework CFNetwork -framework CoreFoundation" --disable-openssl --host=arm-apple-darwin9 --target=arm-apple-darwin9 --prefix=/Users/noone/Documents/siplibs/i386 20 | ``` 21 | 22 | 23 | ### iOS 设备 24 | - osip2 25 | 26 | ``` 27 | $ ./configure CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang CFLAGS="-DPJ_SDK_NAME="\"iPhoneOS7.1.sdk\"" -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk" --host=arm-apple-darwin9 --target=arm-apple-darwin9 --prefix=/Users/noone/Documents/siplibs/arm 28 | ``` 29 | - eXosip2 30 | 31 | ``` 32 | $ ./configure CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang CFLAGS="-DPJ_SDK_NAME="\"iPhoneOS7.1.sdk\"" -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk" LDFLAGS=" -framework MobileCoreServices -framework CFNetwork -framework CoreFoundation" --disable-openssl --host=arm-apple-darwin9 --target=arm-apple-darwin9 --prefix=/Users/noone/Documents/siplibs/arm 33 | ``` 34 | 35 | ## 开始编译 36 | 37 | 通过上文的配置,生成相应的**make**文件,然后在终端中执行如下命令: 38 | 39 | ``` 40 | $make 41 | $make install 42 | ``` 43 | 待编译完毕,即可在我们刚刚通过*--prefix*参数配置的输出目录中找到编译好的哭文件。 44 | 45 | ## 合并静态库 46 | 47 | 仅为真实设备编译一个静态库,会导致项目不能在模拟器上运行,这个项目调试带来很多不便,怎么才能既能在模拟器运行,又能在真机上运行呢?可以分别编译两个目标静态库,然后再将两个库合二为一。合并方法如下: 48 | 49 | ``` 50 | $lipo -create /Users/noone/Desktop/arm/lib/libeXosip2.a /Users/noone/Desktop/i386/lib/libeXosip2.a -output /Users/noone/Desktop/libs/libeXosip2_for_ios.a 51 | ``` 52 | 53 | 以上命令中所涉及的两个libeXosip2.a就是我们刚刚编译的分别对应模拟器和真机的静态库,'-output'参数用于指明输出位置和文件名。 54 | 55 | ## 最后 56 | 将静态库引入项目时,注意还需引入**libresolv.9.dylib**这个依赖库,方能编译成功。 57 | -------------------------------------------------------------------------------- /Compiling/Xcode_command_line_tools .md: -------------------------------------------------------------------------------- 1 | # Xcode command line tools 2 | 3 | ## 安装 4 | 工欲善其事,必先利其器。所以学习之前,先看看如何安装。首先你的有 Xcode,没安装请先移步 App Store 进行下载安装,一键式操作,非常便捷。之后,就可以安装 Command line tools 了,打开 Terminal,直接键入如下命令: 5 | 6 | ```shell 7 | xcode-select --install 8 | ``` 9 | 此时会弹出提示,确认安装,等进度条结束即可。结束后,进入 `/Library/Developer/CommandLineTools/usr/bin/` 查看一下,具体有哪些工具可用。 10 | 11 | 长长一串,有点眼晕啊,但是细心观察可以发现很多都是跟版本控制、Swift、metal相关的,这些我们不去讨论,重点集中在那些通用型较强的工具上。比较多,很多还牵涉到编译到相关知识,不足之处望斧正。 12 | 13 | ## ar 14 | ar 工具应该是来自 architecture 的简写(猜的,未考证),可以通过它对二进制的静态库进行一些编辑操作,例如:对某个架构的静态库进行解包,删除其中的一些目标文件(.O 文件)等等。 15 | 16 | >⚠️ 注意:必须是 Non-fat 静态库,否则会提示类型错误。 17 | 18 | ## lipo 19 | 20 | ## nm 21 | 22 | ## nmedit 23 | 24 | ## size 25 | 26 | ## ranlib 27 | 28 | ## as 29 | 30 | ## cmpdylib 31 | 32 | ## objdmp 33 | 34 | ## libtool 35 | 36 | ## segedit 37 | 38 | ## strip 39 | 40 | ## rebase 41 | 42 | ## otool 43 | 44 | ### otool 支持的架构标识(arch_type),大小端不限: 45 | 46 | ppc64 x86_64 x86_64h arm64 ppc970-64 arm64_32 arm64e ppc i386 m68k hppa sparc m88k i860 veo arm ppc601 ppc603 ppc603e ppc603ev ppc604 ppc604e ppc750 ppc7400 ppc7450 ppc970 i486 i486SX pentium i586 pentpro i686 pentIIm3 pentIIm5 pentium4 m68030 m68040 hppa7100LC veo1 veo2 veo3 veo4 armv4t armv5 xscale armv6 armv6m armv7 armv7f armv7s armv7k armv7m armv7em arm64v8 47 | 48 | ### otool 命令格式: 49 | 50 | otool [-arch arch_type] [-fahlLDtdorSTMRIHGvVcXmqQjCP] [-mcpu=arg] [--version] ... 51 | 52 | ### otool 支持的参数: 53 | 54 | -f print the fat headers 55 | -a print the archive header 56 | -h print the mach header 57 | -l print the load commands 58 | -L print shared libraries used 59 | -D print shared library id name 60 | -t print the text section (disassemble with -v) 61 | -x print all text sections (disassemble with -v) 62 | -p start dissassemble from routine name 63 | -s print contents of section 64 | -d print the data section 65 | -o print the Objective-C segment 66 | -r print the relocation entries 67 | -S print the table of contents of a library (obsolete) 68 | -T print the table of contents of a dynamic shared library (obsolete) 69 | -M print the module table of a dynamic shared library (obsolete) 70 | -R print the reference table of a dynamic shared library (obsolete) 71 | -I print the indirect symbol table 72 | -H print the two-level hints table (obsolete) 73 | -G print the data in code table 74 | -v print verbosely (symbolically) when possible 75 | -V print disassembled operands symbolically 76 | -c print argument strings of a core file 77 | -X print no leading addresses or headers 78 | -m don't use archive(member) syntax 79 | -B force Thumb disassembly (ARM objects only) 80 | -q use llvm's disassembler (the default) 81 | -Q use otool(1)'s disassembler 82 | -mcpu=arg use `arg' as the cpu for disassembly 83 | -j print opcode bytes 84 | -P print the info plist section as strings 85 | -C print linker optimization hints 86 | -------------------------------------------------------------------------------- /Python/Python_notes.md: -------------------------------------------------------------------------------- 1 | # 【整理】Python 笔记 2 | 3 | Python 其实已经断断续续使用很久了,也写过博客项目和小的爬虫项目,但是因为不是主要的开发语言,有些知识点总是随看随忘,如今创笔记一篇,与遗忘抗争到底。 4 | 5 | ### 1. Python 虚拟环境 6 | 7 | 为什么要虚拟环境?虚拟环境可以为每个Python工程提供一个独立的运行环境。 8 | 9 | 首先安装 virtualenv,再生成虚拟环境目录 10 | 11 | ``` Shell 12 | $ pip3 install virtualev 13 | $ cd my_project_dir // 项目目录 14 | $ virtualenv .env // 这里的'.env'是为虚拟环境指定的目录名,否则会生成到当前目录 15 | ``` 16 | 17 | 还可以通过如下命令为虚拟环境指定 Python 解释器 18 | 19 | ``` Shell 20 | $ virtualjenv -p /usr/bin/python2.7 .env 21 | ``` 22 | 23 | 准备好虚拟环境,要怎么使用呢,通过下面的命令激活: 24 | 25 | ``` Shell 26 | $ source .env/bin/activate 27 | ``` 28 | 激活后的操作,例如安装新的依赖包等,都是在当前激活的虚拟环境进行,不会对其它环境,或者系统默认的造成影响 29 | 30 | 退出当前虚拟环境: 31 | 32 | ``` Shell 33 | $ source .env/bin/deactivate 34 | ``` 35 | 36 | 37 | 38 | 39 | ### 2. Python 工程目录结构 40 | 41 | ``` Python 42 | .tx/ 如果你使用Transifex进行国际化的翻译工作,创建此目录 43 | config Transifex的配置文件 44 | $PROJ_NAME/ 按照你实际的项目名称创建目录。如果有多个子项目,就创建多个目录 45 | docs/ 项目文档 46 | wiki/ 如果有wiki,可以创建此目录 47 | scripts/ 项目用到的各种脚本 48 | tests/ 测试代码 49 | extras/ 扩展,不属于项目必需的部分,但是与项目相关的sample、poc等,下面给出4个例子: 50 | dev_example/ 51 | production_example/ 52 | test1_poc/ 53 | test2_poc/ 54 | .gitignore 版本控制文件,现在git比较流行 55 | AUTHORS 作者清单 56 | INSTALL 安装说明 57 | LICENSE 版权声明 58 | MANIFEST.in 装箱清单文件 59 | MAKEFILE 编译脚本 60 | README 项目说明文件,其他需要的目录下也可以放一个README文件,说明该目录的内容 61 | setup.py python模块的安装脚本 62 | ``` 63 | 64 | [参考](http://www.cnblogs.com/holbrook/archive/2012/02/24/2366386.html) 65 | 66 | ### 3.with 关键字 67 | 68 | with 语句是从 Python 2.5 开始引入的一种与异常处理相关的功能(2.5 版本中要通过 from \_\_future__ import with_statement 导入后才可以使用),从 2.6 版本开始缺省可用(参考 What's new in Python 2.6? 中 with 语句相关部分介绍)。with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。([出自:浅谈 Python 的 with 语句](https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/)) 69 | 70 | ### 4. 模块、包、库 71 | 72 | #### 模块(module) 73 | 自我包含且有组织的代码片段。例如:hello.py,这个文件就是一个模块,hello 即为模块名字。 74 | 75 | 模块属性 `__name__` 的值是由 Python 解释器设定的,如果作为主程序调用,那么就会设为 `__main__`,如果是被倒入的,那么就是文件名。可以通过内建函数 `dir()` 查看模块定义了哪些名字,包括变量名、模块名、函数名等。 76 | 77 | #### 包(package) 78 | 包是一个有层次的文件目录接哦股,其中包含了 n 个模块或者 n 个子包,可以构成一个 python 应用的执行环境。 79 | 80 | 包的目录下必须有一个 `__init__.py` 文件,如果包内某个文件目录下还有 `__init__.py`,那么该目录就是一子包。 81 | 82 | ### 库(library) 83 | 库不是 Python 的概念,是借鉴自其它编程语言,通常指具有某种功能完备的包或模块的集合。 84 | 85 | ### 5. 测试覆盖 86 | 87 | 函数中的代码只有在函数被调用的情况下才会运行。分支中的代码,如 `if`(Python 没有 `Swich case`)块中的代码,只有在符合条件的情况下才会运行。测试应该覆盖每个函数和每个分支。越是接近 100% 的测试覆盖,越能保证修改代码后不会出现意外。 88 | 89 | 测试工具:[pytest](https://pytest.readthedocs.io/)、[coverage](https://coverage.readthedocs.io/) 90 | 91 | ### 6. 模块引用及查找路径 92 | TODO:模块引用及查找路径相关。 93 | setup.py 文件如果不存在,或者明明错误,会出现 pytest 找不到模块的问题。(???) 94 | 95 | ### 7. pytest原理 96 | 为什么 setup.py 文件命名错误会导致找不相应包的问题? -------------------------------------------------------------------------------- /OSFoundation/About_locks_I.md: -------------------------------------------------------------------------------- 1 | #【笔记】多核处理器上的锁 2 | 3 | 在传统单处理器环境下,虽然系统可以通过时间片轮询(Round-Robin)与优先级抢占(Priority Preemption)方式快速地在不同进程间进行切换,从而造成一种多进程并发执行的假象,但是实际上一次只能运行一个进程。 4 | 5 | 因此,我们看到经典的操作系统同步原语(Synchronization Primitive),诸如互斥体(Mutex)、信号量(Semaphore)、消息队列(Message Queue)等等均是基于抑制软硬件中断或上下文切换来做到多线程同步的。 6 | 7 | 那么多核处理器上的锁与传统单核处理器上的锁有什么不同呢?这些锁是如何实现的?要进行同步操作时又该如何选择呢? 8 | 9 | ## 原子性 10 | “原子性(atomic)”是指,一组操作在执行时作为一个整体进行,不会被打断。之所以能保证这些操作在执行时不会被中断,是因为来自多个处理器核心对同一个存储空间的访问,存储器控制器会去仲裁当前哪个原子操作先进行访存操作,哪个后进行,这些访存操作都会被串行化。 11 | 12 | ![原子性](https://upload-images.jianshu.io/upload_images/8136508-af1ca825337da627.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000) 13 | 14 | 上图中,假定线程 A 与线程 B 在不同的核心上并行执行。第一段采用的是普通读写方式对共享存储单元进行修改的,而第二种则使用了原子的“读-修改-写”操作。第一段所采用的普通读写方式会出什么问题呢?从时序图上我们可以看到,假定线程 A 与线程 B 几乎同时先对共享存储单元进行加载(读取),然后再几乎同时进行计算。由于线程 A 计算速度比较快,它先将计算好的结果存储(写入)到了共享存储单元中;而此后,线程 B 才计算好,再将它计算好的结果存储到该共享单元中,这就导致了数据不一致!因为线程 B 最终所写回的结果是基于一开始线程 A 对该共享存储单元修改之前的,它这么一改就把线程A对共享存储单元的操作给抹掉了。因此这里正确的做法应该是线程 B 必须基于线程 A 所修改完的结果再对共享存储单元进行操作。 15 | 16 | 第二段采用原子操作则不会有数据不一致问题,无论是线程 A 先对共享存储单元修改完还是线程 B 先修改完,最终结果都是经过这两个线程的计算操作的。CPU 系统总线仲裁器会去裁决哪个先执行,哪个后执行,而后执行的那个会被阻塞(等待)直到先前的操作完成。 17 | 18 | >图片及说明引自 [C11标准的原子操作详解](https://www.jianshu.com/p/ae1f912a7607)。 19 | 20 | 因此,在多核心多线程并行计算的环境下,原子操作是唯一的数据同步手段。另外在此环境下,像互斥体(mutex)、信号量(semaphore)等同步原语的实现也都基于原子操作。 21 | 22 | > 原语——在计算机科学中,原语指编程语言中最简单的可用元素。它是目标机器上最小的“可编程单元”。[维基](https://en.wikipedia.org/wiki/Language_primitive) 23 | 24 | ## 信号量 25 | 26 | 信号量是由 Edsger Dijkstra 最先提出的,通过一种叫做信号量(semaphore)的特殊类型的变量,实现同步不同执行线程。信号量 s 是具有非负整数值的全局变量,只能由两个特殊的操作来处理,P 和 V: 27 | 28 | - P(s):如果 s 是非零的,那么 P 将 s 减 1, 并且立即返回。如果 s 为零,那么就挂起这个线程,直到 s 变为非零,而一个 V 操作会重启这个线程。在重启之后,P 操作将 s 减 1, 并将控制返回给调用者。 29 | - V(s):V 操作将 s 加 1.如果有任何线程阻塞在 P 操作等待 s 变成非零,那么 V 操作会重启这鞋线程中的一个,然后该线程将 s 减 1,完成它的 P 操作。 30 | 31 | P 中的测试和减 1 操作时不可分割的,也就是说,一旦预测信号量 s 变为非零,就会将 s 减 1,不能有中断。同理 V 中的加 1 操作也是不可分割的,也就是加载、加 1 和存储信号量的过程中没有中断。 32 | 33 | > P 和 V 名字的起源:Edsger Dijkstra(1930-2002)出生于荷兰。名字 P 和 V 来源于荷兰语单词 Proberen(测试)和 Verhogen(增加)。 34 | 35 | 下面是 Posix 标准中定义的信号量函数: 36 | 37 | ```c 38 | #include 39 | 40 | int sem_init(sem_t *sem, 0, unsigned int value); 41 | int sem_wait(sem_t *s); /* P(s) */ 42 | int sem_post(sem_t *s); /* V(s) */ 43 | ``` 44 | 45 | ## 锁的实现 46 | 了解完锁的基础,我来看看一些常见锁的实现: 47 | 48 | ### 互斥锁 49 | 50 | ### 自选锁锁 51 | 52 | ### 读写锁 53 | 54 | 55 | ## 锁的分类 56 | 57 | 在讲分类之前,先来明确一个概念。互斥锁(Mutex, mutual exlusion lock),它是一个通用术语,用来表示具有强制排它访问语义的原语。 58 | 59 | >原语([Language primitive](https://en.wikipedia.org/wiki/Language_primitive)) 60 | 61 | 62 | 平时接触到的锁有很多名字: 63 | 64 | - 阻塞锁: 65 | - 忙等锁: 66 | - 复杂锁: 67 | 68 | ## 锁的选择 69 | ### 避免死锁 70 | 71 | 大多属复杂的锁都可以被实现为阻塞锁或者复杂的自旋锁,而不会影响懂啊他们的功能或者接口。 72 | 73 | 74 | ## 后记 75 | 介绍锁的文章恐怕多的恐怕牛毛都要望其项背了,但是放眼望去,大多都是简单的罗列一下各个锁,然后在介绍一些用法,无外乎如此。好一点的在对比一下性能。曾几何时,在自己拜读完多篇关于锁的文章后,产生一种错觉,“原来锁并不复杂”。现在回头看来实在是肤浅幼稚。 76 | 77 | 随着对计算机知识的学习,深知自己还处在领域的底层,所以越来越不敢冠上原创二字,因为遇到的问题都有现成的解决方案。 78 | 79 | 80 | ## 参考 81 | 82 | - 《深入理解UNIX系统内核》 Uresh Vahalia 著,李雨、薛磊、黄庆新 译 83 | - 《C语言编程魔法书:基于 C11 标准》 陈轶 著 84 | - [C11标准的原子操作详解](https://www.jianshu.com/p/ae1f912a7607) 85 | -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_third_library.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之常用三方框架 2 | 3 | ## 1. 网络库 4 | 5 | ### 1.1 AFNetworking,是否支持IPv6 6 | 3.0 之后支持 7 | 8 | ### 1.2 AFNetwoking 为什么添加一条常驻线程? 9 | 网络请求是异步的,这会导致获取到请求数据时,线程已经退出,代理方法没有机会执行。因此,AFN 的做法是使用一个 runloop 来保证线程不死~ 10 | 然而频繁的创建线程并启动runloop肯定会造成内存泄露(runloop 无法停止.线程无法退出) 11 | 所以 AFN 就创建了一个单例线程,并且保证线程不退出. 12 | 13 | >[来源参考](https://www.jianshu.com/p/7170035a18e8) 14 | 15 | > 这个问题是在 AFNetworking 未更新到 3.0 之前时存在的,主要原因在与 NSURLConnection。在 Apple 推出 NSURLSession 后,作者果断的摒弃了旧方式,转而使用 NSURLSession,因为它所带来的优化太多了,复用、多连接、管道等等。 16 | 17 | ### 1.3 AFNetworking 的 reachability 是如何检测到网络状态变化的? 18 | 要获取设备状态,必然少不了引入 `SystemConfiguration.framework` 框架。AFNetworking 自然也不例外,它创建一个 `SCNetworkReachabilityRef`,并将其添加到主线程的 RunLoop 中,与 `kCFRunLoopCommonModes` 关联。这样当状态发生变化时,就会收到回调。 19 | 20 | ## 2. 图片库 21 | 在 Swift 诞生之前最著名的图片库时 SDWebImage,在 Swift 诞生后,又出现了一个原生的 Kingfisher。 22 | 23 | ### 2.1 SDWebImage 图片编解码 24 | SDWebImage 设置有单独的解码模块,当向缓存请求某个图片时,如果命中,那么此时就会对图片数据进行解码操作,然后在返回。因为请求是在后台线程中进行的,所以也就避免了在赋值时在主线程进行解码,今儿因为解码而出现阻塞的问题。如果不明中,会进入下载流程,下载完成后,在重复之前的查询过程。 25 | 26 | ## 3. 模型库 27 | 日常开发中最长用到的模型库就是“缓存”,而提到缓存就不得不说到 YYCache。从各方面来讲,它都是一个十分优秀的开源库,非常值得学习。此外还有 YYMode、YYImage、YYAsyncLayer,也都非常值得深入学习。 28 | ### 3.1 YYCache 29 | YYCache 采用最近最少使用(Least Recently Used,LRU)算法。它综合了链表的快速编辑,以及哈希表的快速查询特性,以实现 LRU 算法。 30 | 31 | ### 3.4 YYImage 图片编解码 32 | 与 SDWebImage 的解码逻辑类似,也是放到后代队列进行预先解码,以避免在赋值后由主线程队列解码。 33 | 34 | ## 4. 数据库 35 | 36 | ### 4.1 SQLite 数据的读写特性 37 | SQLite 支持无限多个读取,但是任一时刻仅支持一个写入,对于多个写入请求是通过队列实现的。此外,SQLite的初衷不是取代或者看齐那些大型 C/S 数据库,而是 `fopen()`。 38 | 39 | ### 4.2 FMDB 是如何实现同步的 40 | 通过一个 serial queue 实现对数据库的同步访问。此外如果是只读数据库,则可以通过FMDBdatabasePool 进行真正的并发访问。 41 | 42 | ## 5. 三方库管理工具 43 | 44 | ### 5.1 Pod 的工作原理 45 | CocoaPods的工作主要是通过ProjectName.xcworkspace来组织的,在打开ProjectName.xcworkspace文件后,发现Xcode会多出一个Pods工程。 46 | 47 | 1. 库文件引入及配置:库文件的引入主要由Pods工程中的Pods-ProjectName-frameworks.sh脚本负责,在每次编译的时候,该脚本会帮你把预引入的所有三方库文件打包的成ProjectName.a静态库文件,放在我们原Xcode工程中Framework文件夹下,供工程使用。 48 | 如果Podfile使用了use_frameworks!,这是生成的是.framework的动态库文件。引入方式也略有不同。 49 | 2. Resource文件:Resource资源文件主要由Pods工程中的Pods-ProjectName-resources.sh脚本负责,在每次编译的时候,该脚本会帮你将所有三方库的Resource文件copy到目标目录中。 50 | 3. 依赖参数设置:在Pods工程中的的每个库文件都有一个相应的SDKName.xcconfig,在编译时,CocoaPods就是通过这些文件来设置所有的依赖参数的,编译后,在主工程的Pods文件夹下会生成两个配置文件,Pods-ProjectName.debug.xcconfig、Pods-ProjectName.release.xcconfig。 51 | 52 | ### 5.2 Pod update和 Pod install 的区别 53 | **结论** 54 | 55 | - `pod install` 用于向你的工程安装新的 pods,那怕 `Podfile` 已经存在,且已运行过 `pod install`;即使,你仅仅是通过 CocoaPods 对 pods 进行增/删(编辑)。 56 | - `pod update [PODNAME]` 只有在你想要升级 pods 到新版本时,才应用使用它。 57 | 58 | **pod install** 59 | 60 | 根本区别就在于 `Podfile.lock` 文件,它会在首次调用 `pod install` 时生成。该文件用于跟踪、锁定已安装的每个 pod 的版本,所以版本控制时最好也将它一并提交,这样就能保证所有分发到副本都能安装到一致的依赖。 61 | 62 | 当再次调用 `pod install` 命令时,它将只解析那些尚未被 `Podfile.lock` 跟踪的依赖。对于已经存在于 `Podfile.lock` 中的依赖,则只是下载对应的版本,而不会检查是否有更新。对于新增的依赖,则会根据你在 `Podfile` 中的描述下载对应版本。(例如:pod 'MyPod', '~>1.2') 63 | 64 | **pod outdated** 65 | 66 | 理解了 `Podfile.lock`,这个命令容易了,它会列出那些相对于 `Podfile.lock` 现存记录有更新的依赖库。也就是说,如果此时你运行 `pod update PODNAME`,相应的依赖将更新到新版本,新版本与你在 `Podfile` 中指定的版本相对应,例如:pod 'MyPod', '~>x.y'。 67 | 68 | **pod update** 69 | 70 | 如果运行 `pod update PODNAME`,CocoaPods 会忽略 `Podfile.lock` 中的版本记录,而直接尝试查询并更新对应的依赖。它会将依赖更新到尽可能新的版本(不会高过 Podfile 中的限制,如果有的话)。 71 | 72 | 如果运行时为制定具体依赖,那么它将更新 Podfile 中列明的所有依赖。 73 | 74 | ## 6. 其它 75 | 76 | -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_java_from_ali.md: -------------------------------------------------------------------------------- 1 | # Java 阿里面试题 2 | TreeSet/HashSet 区别 3 | HashMap 如何解决冲突,扩容机制 4 | ConcurrentHashMap 如何做到高并发的 5 | 线程池平常怎么用 6 | 多个线程等待到某一节点然后统一放行有几种实现方式? 7 | 数据库索引结构 8 | select * from t where a=? and b>? order by c limit 0,100 如何加索引 9 | 什么是聚簇索引和非聚簇索引 10 | 介绍下 Fescar/了解 CAP 吗?redis 里的 CAP 是怎样的? 11 | 如何理解幂等?项目中接口的幂等是如何做的? 12 | 算法题:两个有序的 list,求交集 13 | 蚂蚁金服财富 二面(两次电话面试) 14 | 15 | 第一次电面 16 | 17 | 解释下乐观锁悲观锁 18 | JVM 判断对象是否回收? 19 | 反射能获得类里面方法的名称吗?参数名称呢?参数类型呢? 20 | 动态代理的实现方式?CgLib 和 jdk 的代理有什么区别? 21 | 分布式锁有哪些主流实现方式?redis 和 zk 锁有什么区别? 22 | ThreadLocal 作用是什么?说下用法 23 | 设计秒杀系统要考虑哪些点? 数据预热,CDN 缓存,超卖问题,流量削峰 24 | 从性能角度上如何保证秒杀系统稳定? 觉得应该是预热,削峰,库存扣减的操作放到缓存,减少数据库的访问 25 | A,B 系统转账如何保证分布式数据一致性? 26 | 你有什么想问的? 27 | 第二次电面 28 | 29 | 简单介绍下自己的近况 30 | 对 kafka 了解吗?Rocketmq 事务性消息怎么实现的? 31 | 假设事务提交的消息丢了,没有发到 broker 上面,会怎么处理? 32 | 分布式事务一致性如何保证? 33 | 二阶段提交解释下,如果本地事务超时会怎样处理?一阶段都可以 commit,二阶段的时候协调器发送全局提交的消息,这个时候消息发送失败了会怎么处理? 34 | TCC 对异常流是如何操作的? 35 | 为什么要看开源代码? 36 | 最终一致性如何实现的? 37 | 有没有遇到过死锁? 38 | A 往 B 转钱,B 往 A 转钱,同时的会死锁吗?如何解决死锁? 39 | 设计一个全局唯一流水号? ​ https://tech.meituan.com/2019/03/07/open-source-project-leaf.html 40 | 设计幂等方案防止重复提交? ​ 进页面的时候生成一个 token,请求带 token 过来,校验 token 41 | 大数相加 42 | 工厂方法模式一般如何实现? 43 | 单例模式 44 | 其他的设计模式 45 | 再答一下上次的秒杀系统 46 | 写金融类的系统有什么需要关注的地方? 47 | 非功能性的设计关注那些? ​ 其实想问你日志规范,代码规范之类的 48 | 你有什么想问的? 49 | 蚂蚁三面 50 | 51 | 如何提前发现你的数据有问题,而不是等到用户反馈才知道? 52 | 53 | 如何防止超卖? 54 | 55 | 为什么要用 Redis?为什么没有用 db? 数据量不小,查一次耗时长 56 | 57 | 有没有 QPS?(没有统计过指标) 58 | 59 | 如何部署?(阿里云采购) 60 | 61 | 发生过丢消息的情况吗?为什么会丢失? rocket 保证消息不丢失 62 | 63 | 项目的过程中哪个点比较难? 64 | 65 | 项目中为什么要用 ThreadLocal 去做租户的隔离? 66 | 67 | 项目有什么缺点? 68 | 69 | 100 亿行数据,每个数字 32 位,取最小的的数字 70 | 71 | 有没有碰到特别难的事情,如何解决的? 72 | 73 | 业界中间件有什么了解吗?讲一个你深度理解原理的. 74 | 75 | 高并发的问题有遇到过吗?分布式锁是排他的,如何提升存储效率? ​ 遇到的不多,但如果请求比较多的话会用分布式锁来解决,这么说也不对.应该是递进关系的,后面就给自己挖坑了 ​ 分布式锁是排他的,如何提升存储效率,应该是吞吐量的意思吧 76 | 77 | 有遇到过很大的流量吗? ​ 没有 78 | 79 | 描述产生一次 fullGC 的整个过程 ​ fullGC 解析 80 | 81 | 平时通过什么来提升自己 ​ 通过博客 /技术书籍 82 | 83 | 你有什么问题? (您觉得我的短板在于?) ​ 没有互联网的思维,不会估算系统容量就往上发,这怎么能行,数据双写如何保证一致 ​ 84 | 85 | 蚂蚁金服国际事业群一面 86 | 87 | 头一天约了时间,第二天准时打电话过来了,整体下来偏基础,发现自己 JVM 相关菜的一笔,要好好补补,重新看一遍深入理解 java 虚拟机了 88 | 89 | 简单介绍下自己和项目 blablabla.... 90 | 91 | 你觉得项目里最大的挑战是什么? 答得项目的设计和推动 92 | 93 | 为什么选择用 rocketmq? 公司推的呗 94 | 95 | 对 rocketmq 的了解?基本原理 发布订阅,服务注册,消息丢失的情况(几种情况:producer down,broker down,consumer down) 96 | 97 | 消息如何顺序? 单一队列,多队列分区顺序 98 | 99 | rocketmq 事务消息,producer 如果 down 掉,事务无法提交这种情况 https://github.com/apache/rocketmq/blob/master/docs/cn/design.md 100 | 101 | rocketmq 是强一致性还是弱一致性 102 | 103 | 消息重复如何解决?可以在中间件层解决吗?MQ 体系协议了解吗? 业务层幂等,中间件层 kafka 有做过这个处理,不了解 104 | 105 | ThreadLocal 是怎样的概念?如何实现线程隔离的?基于这个原理有没有更加优化的方式? ​ 这个优化让我懵逼了 106 | 107 | 线程池用 ThreadLocal 有什么问题?有什么思路来让业务方不去关注 ThreadLocal 的 set ​ ThreadLocal 在线程复用的时候值可能不是最新的,需要每次都 set 108 | 109 | 你说你用过 dubbo,那看过 netty 源码吗? 110 | ​ 不好意思没有....(那你回去看一下 netty 里的 FastThreadLocal,对 threadLocal 有优化) ​ https://blog.csdn.net/TheLudlows/article/details/85267651 111 | 112 | 讲讲 dubbo 的基本原理 113 | 114 | JVM 调优? 115 | ​ 没做过,说了下公司的一些 JVM 参数 116 | 117 | 频繁 YGC 如何排查? 118 | ​ 支支吾吾半天.... 119 | 120 | 换个问题,为什么会发生 YGC? 121 | 122 | 如果知道哪些对象需要被回收? 123 | ​ GCROOT 124 | 125 | GCROOT 有哪些? 126 | 127 | 栈和堆有什么区别? 128 | 129 | 什么时候会压栈? 130 | 131 | 程序都是线程在执行,线程和栈和堆的关系? ​ 懵逼了,后来发现是想问,栈线程私有,堆公共,一时没想起来 132 | 133 | HashMap 如何实现?为什么会变红黑树呢?如何扩容?为什么是两倍?什么时候会用到位运算? 134 | ​ hashmap 到处都是位运算,居然只答出来一个扩容的,脑残 ​ indexFor 取位置的时候也是用的位运算 h&length-1 ​ 有提到扩容的问题.扩容两倍的原因,也是为了取模可以通过上面这个&操作来做提升性能,%据说慢十倍,咱也不知道,咱也不敢问 ​ https://blog.csdn.net/u014532901/article/details/78936283 135 | 136 | 你有什么想问的? -------------------------------------------------------------------------------- /Python/Flask/Flask_notes_03.md: -------------------------------------------------------------------------------- 1 | # 【整理】Flask 学习笔记之三 2 | 3 | 以前都是手动部署,今天接触到自动部署,特此记录。 4 | 5 | ### 项目可安装化 6 | 7 | 什么是可安装化?是指创建一个项目**发行**文件,以使项目可以安装到其它环境,类似在自己的项目中安装 Flask 一样,可以让项目接受标准工具的管理。 8 | 9 | 可安装化的优点: 10 | 11 | - 可以从任何地方导入项目并运行 12 | - 可以和其它包一样管理项目的依赖,即使用 `pip3 install yourproject.whl` 来安装项目并安装相关依赖 13 | - 测试工具可以分离测试环境和开发环境 14 | 15 | ### 描述项目 16 | 17 | `setup.py` 文件是一个可供 `setuptools` 运行的脚本,它描述了项目(包)的名字和版本等信息,以及需要包含哪些文件。下面是一个描述文件的内容: 18 | 19 | ``` Python 20 | import setuptools 21 | 22 | with open("README.md", "r") as fh: 23 | long_description = fh.read() 24 | 25 | setuptools.setup( 26 | name="name-of-project", 27 | version="0.0.1", 28 | author="author", 29 | author_email="author@example.com", 30 | description="A small example package", 31 | long_description=long_description, 32 | long_description_content_type="text/markdown", 33 | url="https://github.com/example/exampleproject", 34 | packages=setuptools.find_packages(), 35 | include_package_data=True, 36 | zip_safe=False, 37 | install_requires=[ 38 | 'flask', 39 | ], 40 | extras_require={ 41 | 'test': [ 42 | 'pytest', 43 | 'coverage', 44 | ], 45 | }, 46 | classifiers=[ 47 | "Programming Language :: Python :: 3", 48 | "License :: OSI Approved :: MIT License", 49 | ], 50 | ) 51 | ``` 52 | 键值说明: 53 | 54 | - pakcages: 是一个 `list` ,用于列明该项目所有导入的已发布依赖,为了免去手动罗列的烦恼,这里通过 `find_packages()` 函数自动查找并生成该列表; 55 | - inlcude_package_data:因为要包含静态文件和一些模板文件,所以这里设置为 True; 56 | - zip_safe:是否压缩为一个 egg 文件,否则以文件夹的形式安装; 57 | - install_reuires: 指明安装必备的依赖包; 58 | - extras_require: 指明额外的一些依赖; 59 | - classifiers:用于向 pip 描述一些额外信息,例如,这里指明项目仅适配 python 3,以 MIT 协议发布。[classifiers](https://pypi.org/classifiers/) 60 | 61 | 很多键的自描述性都非常好,所以这里仅简单列举几个,如想了解更多请[参见](https://packaging.python.org/guides/distributing-packages-using-setuptools/) 62 | 63 | 因为上面设置了 `include_package_data=True` 所以还需要一个 `MANIFEST.in` 文件,来指明需要包含的文件: 64 | 65 | ``` 66 | include flaskr/schema.sql 67 | graft flaskr/static 68 | graft flaskr/templates 69 | global-exclude *.pyc 70 | ``` 71 | 命令说明: 72 | 73 | | 命令| 说明 | 74 | |:---|:-----| 75 | |include pat1 pat2 ... |引入所有列明的文件| 76 | |exclude pat1 pat2 ...|排除引入所有列明的文件| 77 | |recursive-include dir pat1 pat2 ...|引入指定目录下所有相匹配的文件| 78 | |recursive-exclude dir pat1 pat2 ...|引入指定木下所有文件,排除特定文件| 79 | |global-include pat1 pat2 ...|引入项目目录树下所有指定的文件| 80 | |global-exclude pat1 pat2 ...|排除项目目录树中所有指定的文件| 81 | |prune dir|排除指定目录下的所有文件| 82 | |graft dir|引入指定目录下的所有文件| 83 | 84 | ### 安装项目 85 | 86 | 在虚拟环境中通过 `pip3 install -e` 安装项目。该命令告诉 pip 在当前的文件夹中寻找 `setup.py` 并在编辑或开发模式下安装。所谓编辑模式是指,档改变本地代码后,只需要重新??? 87 | 88 | 可以通过 `pip3 list` 查看项目的安装情况: 89 | 90 | ``` 91 | Package Version Location 92 | -------------- ------- ----------------------------------------------- 93 | atomicwrites 1.3.0 94 | attrs 19.1.0 95 | Click 7.0 96 | coverage 4.5.2 97 | Flask 1.0.2 98 | flaskr 1.0.0 /Users/username/Documents/Python/blog-with-flask 99 | itsdangerous 1.1.0 100 | Jinja2 2.10 101 | MarkupSafe 1.1.1 102 | more-itertools 6.0.0 103 | pip 10.0.1 104 | pluggy 0.9.0 105 | py 1.8.0 106 | pytest 4.3.0 107 | setuptools 39.0.1 108 | six 1.12.0 109 | Werkzeug 0.14.1 110 | ``` 111 | 112 | ### 参考 113 | 114 | - [Flask 教程](https://dormousehole.readthedocs.io/en/latest/tutorial/install.html) 115 | - [Packaging and distributing projects](https://packaging.python.org/guides/distributing-packages-using-setuptools/) -------------------------------------------------------------------------------- /iOS/Something_more_about_off_screen_rendering.md: -------------------------------------------------------------------------------- 1 | # 由离屏渲染引申开去 2 | 3 | 一直对离屏渲染有疑惑,始终没能解开,归根结底还是自己基础薄弱,对计算机图形理解的不够。比较巧的是,前天即刻团队突然在网上分享了一篇[《关于iOS离屏渲染的深入研究》](https://zhuanlan.zhihu.com/p/72653360),一时间获得了不错的转发,果断第一时间拜读,获益颇多,不得不说他们确实花费了不少功夫。但遗憾的是作者对于离屏渲染发生时机,以及为什么不得不“离屏”的原因没能深入阐述,给出一个明确的答案,只是笼统的解释为“不能在单次遍历中完成渲染”。这不但没能打消自己先前的疑虑,反而更加困惑了。所以决定自己动手探究一番。 4 | 5 | > ⚠️注意:该文只是自己在探寻离屏渲染过程中的阶段性总结,不能保证正确性,仅供参考。错误之处还望指正。 6 | 7 | ## 太长不想看 8 | 离屏渲染发生在渲染流程中最后的“融合”阶段(包括 stencil 和 blend)。假设有10层待选像素需要融合,默认是从低向上逐层进行的,但是因为“透明”、“遮罩”等效果,不得不需要先融合后面的,然后再与前面的结果进行融合,而这个不得不就是导致离屏渲染的直接原因。因为既要保存先前的融合结果,又要另辟一块地方以便对后面的像素进行融合操作,这就导致了所谓的“离屏”,因为融合过程是发生在帧缓存中的,这块另辟的空间显然需要脱离当前的缓存。 9 | 10 | 如果看过之后还不甚明了,那么请继续。 11 | ## 两个概念 12 | 在继续之前,我们先来统一两个概念,可能与你之前的理解有所不同。 13 | 14 | #### 帧缓存 15 | 与我们平时接触到的缓存概念类似,GPU 也需要一块空间用来存储渲染出来的 2D 图像像素数据。这个接收渲染结果的缓冲区叫做帧缓存(frame buffer)。 16 | 17 | 可以存在多个帧缓存,也可以通过 OpenGL 让 GPU 把渲染结果存储到任意数量多帧缓存中。但是,屏幕显示像素要收到保存在 18 | 19 | #### 什么是离屏渲染? 20 | 这里的离屏渲染是指离开当前屏幕帧缓存发生的由硬件加速的渲染处理(严格来说应该是后帧缓存,具体说明请参见 OpenGL 渲染流程一节)。 21 | 22 | 那么通过 Core Graphics 框架发生的渲染是不是离屏渲染呢?广义上是的,因为它也不是发生在当前屏幕的帧缓存,而是 CPU 单独开辟一块空间进行的。为了与更擅长渲染工作的 GPU 进行区分,这种渲染方式被称为“软渲染”,与“软解码”概念类似。 23 | 24 | #### Pipeline 25 | 个人猜测,该词很可能来源于工业。石化工业中通过管道将不同处理环节串接起来,人们从几个固定的点对处理过程进行干预,直至最终得到产品。很多书籍中把它直译为“管线”,但是由于中文的语境(context)非常重要,因此直译很容易让人困惑(反正笔者初遇时是迷惑了很久),所以这里把它意译为“流程”或“流水线”,指一系列的承前启后的处理过程。 26 | 27 | ## UIKit 渲染流程 28 | 早期 UIKit 的硬件渲染引擎就是建立在 OpenGL 基础之上的,如下: 29 | 30 | ![UIKit Architecture Old](../images/ca_architecture_old.png) 31 | 32 | > 随着 Metal 框架的发布,UIKit 的硬件渲染引擎也随之被替换。Metal 的渲染流程还和 OpenGL 一样吗?不一样,会有哪些变化呢?还有待进一步的考证。 33 | 34 | OpenGL 是使用客户端-服务端即 C/S 的形式实现的,很显然 UIKit 也采用了这一模式。我们的 App 可以看作客户端,而 Core Animation 提供的 OpenGL/Metal 实现看作服务端。 35 | 36 | ![Core Animation Pipline](../images/core_animation_pipline.png) 37 | 38 | > 上图引用自 WWDC 2014 “Advanced Graphics and Aniamtions for iOS Apps”,目前该分享的视频在官方网站上暂时搜索不到,但是一个不存在视频网站上有,欲了解详情,请移步。 39 | 40 | 从图中可知具体的渲染处理都是有 Render Server 负责的,通过公开的架构图可知: UIKit 的 Render server 正是通过 OpenGL 来实现的。接下来,我们就来了解下 OpenGL。 41 | 42 | ## OpenGL 43 | 44 | ### 什么是 OpenGL 45 | OpenGL 是一种应用程序编程接口,它是一个可以对图形硬件设备特性进行访问的软件库,可以用于设置所需的对象、图像和操作,以便开发交互式的三维计算机图形应用程序。 46 | 47 | ### OpenGL 渲染流程 48 | 49 | ![OpenGL_pipline_A](https://open.gl/media/img/c2_pipeline.png) 50 | 51 | 如上图所示,渲染流程大致分为这么几个阶段。前五个环节与本文讨论内容的关系不是那么密切,所以这里就不展开了,欲了解详情,请详阅参考中所累文章。 52 | 53 | 接下来我们看下重点,“Test and blending”。 54 | 55 | ### 测试(Test) 56 | 测试分为两种: 57 | - Depth test: 58 | - Stencil test: 59 | 60 | 61 | ### 帧缓存 62 | 63 | 64 | ### 在这个阶段会使用深度测试(depth test,或者通常也称作 z-buffering)和模板测试(stencil test)的方式来决定一个片元是否可见。 65 | 66 | ### 融合(blending) 67 | 68 | 69 | 有了上面这些 OpenGL 渲染流程的基础,我们在来看一看 UIKit 是如何渲染? 70 | 71 | 72 | ### 前后帧缓存 73 | 74 | 75 | 76 | ## 诱因分析 77 | 78 | 79 | ## 透明 80 | 双层透明 81 | 82 | ### 遮罩 83 | 84 | 85 | 上面的理论假设已经完毕,下面我们来看看如何证明这一假设: 86 | 87 | ## 证明 88 | 89 | ### UIKit pipline 90 | 91 | ### Core Aniamtin guide 92 | 93 | ### 双透明的例子 94 | 95 | ### 圆角裁切的例子 96 | 97 | ### 离屏渲染标黄的截图 98 | 99 | ### 疑问 100 | 101 | - 为什么离屏会有这么高的代价,因为阻塞了主融合“线程”(因为对 GPU 的理解几乎空白,所以这里用线程可能不准确),需要等待子任务的完成吗? 102 | - View 的 mask 会导致离屏渲染吗?有待进一步验证。 103 | 104 | ### 后记 105 | 106 | 107 | 108 | ### 参考 109 | 110 | - [《关于iOS离屏渲染的深入研究》](https://zhuanlan.zhihu.com/p/72653360) 111 | - 《OpenGL 编程指南(原书第8版)》作者:Dave Shreiner、Graham Sellers、John Kessenich、Bill Licean-Kane,译者:王锐 112 | - [Learn OpenGL](https://learnopengl.com) 113 | - [OpenGL Rendering Pipeline](http://www.songho.ca/opengl/gl_pipeline.html) 114 | - [https://open.gl/drawing](https://open.gl/drawing) 115 | - [Morden OpenGL](https://glumpy.github.io/modern-gl.html) 116 | - [WWDC 2018:写给 OpenGL 开发者们的 Metal 开发指南](https://juejin.im/post/5b1e8fade51d45068f65d6e8) 117 | 118 | -------------------------------------------------------------------------------- /macOS/macOS_project_without_xib_or_storyboard.md: -------------------------------------------------------------------------------- 1 | # macOS 项目的纯代码启动 2 | 3 | 很少触及 macOS 上的开发,即使偶尔小试也都是通过 xib,或者 Storyboard 来启动,今天在测试 OpenGL 代码时突发奇想,怎么像在 iOS 上一样通过纯代码启动应用呢? 4 | 5 | > 这里用“启动”这个词不太准确,因为此时 `main` 已经被调用了,只是默认走的 Xib 或 Storyboard 入口(entry point)。但如果叫“入口”,恐怕哪些真正需要了解文章内容的朋友又检索不到了。 6 | 7 | ## 创建项目 8 | 9 | 首先创建一个 macOS 项目,跟着提示一步一步往下走即可。有一点要说明,如果在创建时没有勾选 Storyboard,那么 Xcode 会默认给你创建一个 xib(就是被这点给养懒了)。 10 | 11 | 创建完毕后,我们先来看看工程结构,如下图所示。 12 | 13 | ![项目截图](../images/macos_project_screen_shot.png) 14 | 15 | 可以看到,无论是选择 Swift 还是 Objective-C,两个项目的结构基本是一致的。唯一的区别就是 Swift 项目中没有相应的 `main.swift` 文件。我们打开 `AppDeleagte` 文件,可以看到除了 Swift 文件中多了一个“注解” `@NSApplicationMain`,其它全部相同(Swift 官方文档称之为“(属性)Attribute”)。 16 | 17 | 事实上正是这句“注解”起到了 Objective-C 项目中 `main.m` 文件的 作用,可以把它看过一个语法糖,编译器在解析到此时,会自动帮我们创建一个 main 文件。 18 | 19 | 既然讨论到是纯代码项目,那么自然 xib 也不能有,果断删除。此时运行一下看看。虽然能编译成功,但是应用直接闪退了。单纯的删除 xib 看来是不行的,还需要进一步的设置。那么怎么解决呢? 20 | 21 | ## 修复 22 | 23 | 打开 console 先看看错误提示: 24 | 25 | ```shell 26 | 2019-08-04 14:09:05.357789+0800 WithoutXib[4143:396195] Unable to load nib file: MainMenu, exiting 27 | ``` 28 | “找不到 nib 文件”,嗯,显然 xib 文件虽被删除,但是某一处依然在引用它。回想一下 iOS 的设置,当我们要纯代码项目时,除了删除 xib,还要取消 `Main Interface` 对它的引用。打开项目的通用设置,找到 “Deployment Info”,将其中对 `MainMenu` 的引用删除。再次运行,发现项目已经可以运行了,但是什么窗口都没有。(也可直接编辑“info.plist”文件) 29 | 30 | 接下来,我们看看如何通过代码创建一个窗口。 31 | 32 | ## 创建窗口 33 | 34 | 再次打开 `AppDelegate` 文件,更新为如下内容: 35 | 36 | ```swift 37 | import Cocoa 38 | 39 | @NSApplicationMain 40 | class AppDelegate: NSObject, NSApplicationDelegate { 41 | 42 | let mainWindow: NSWindow = { 43 | let window = NSWindow(contentRect: NSMakeRect(0, 0, 640, 480), 44 | styleMask: [.titled, .resizable, .miniaturizable, .closable, .fullSizeContentView], 45 | backing: .buffered, 46 | defer: false) 47 | window.minSize = NSMakeSize(320, 240) 48 | window.center() 49 | 50 | return window 51 | }(); 52 | 53 | 54 | func applicationDidFinishLaunching(_ aNotification: Notification) { 55 | 56 | mainWindow.makeKeyAndOrderFront(nil); 57 | mainWindow.title = "Main Window Without Xib" 58 | } 59 | 60 | func applicationWillTerminate(_ aNotification: Notification) { 61 | // Insert code here to tear down your application 62 | } 63 | } 64 | ``` 65 | 66 | 再次运行。还没有窗口出现?别急,在我们创建窗口的代码上打个断点,运行,发现根本就没有走这里。怎么回事呢? 67 | 68 | ## 再次修复 69 | 70 | 如果开始创建的是 Objective-C 项目的话,到这里可能解决起来要简单些,因为打开 `main` 文件就会发现: 71 | 72 | ```objc 73 | int main(int argc, const char * argv[]) { 74 | return NSApplicationMain(argc, argv); 75 | } 76 | ``` 77 | 78 | 这里好像跟我们的 `AppDelegate` 没什么关系,在回想下 iOS 的 `main` : 79 | 80 | ```objc 81 | int main(int argc, char * argv[]) { 82 | @autoreleasepool { 83 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 84 | } 85 | } 86 | ``` 87 | 这里我们有指定 AppDelegate。看来问题就在这里了。问题定位到了,可是 Swift 没有 main 文件怎么办,没有就创建一个。内容如下: 88 | 89 | ```swift 90 | import AppKit 91 | 92 | let app: NSApplication = NSApplication.shared() 93 | let appDelegate = AppDelegate() 94 | app.delegate = appDelegate 95 | _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) 96 | ``` 97 | 98 | > 注意,自己手动添加 main 文件后,原来 AppDelegate 中的注解必须删除或注释掉,否则会提示错误。 99 | 100 | 再次运行,阔别已久的窗口终于又出现了。 101 | 102 | 103 | ## 参考 104 | 105 | - [What does “@UIApplicationMain” mean? 106 | ](https://stackoverflow.com/questions/24516250/what-does-uiapplicationmain-mean) 107 | - [How to make a Cocoa application without a .xib file](https://jameshfisher.com/2017/03/17/removing-xib-file/) 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /Interview_questions/question about iOS.txt: -------------------------------------------------------------------------------- 1 | 给出一个字符串,要求将其按照单词顺序进行反转 33 2 | 类(class)和结构体(struct)有什么区别 81 3 | Swift是面向对象还是函数式的编程语言 83 4 | 在Swift中,什么是可选型(Optional) 84 5 | 在Swift中,什么是泛型(Generics) 84 6 | 说明并比较关键词:Open,Public,Internal,File-private和Private 85 7 | 说明并比较关键词:Strong,Weak和Unowned 86 8 | 在Swift中,如何理解copy-on-write 87 9 | 什么是属性观察(Property Observer) 87 10 | 在结构体中如何修改成员变量的方法 88 11 | 如何用Swift实现或(II)操作 89 12 | 实现一个函数:输入是任意一个整数,输出为输入的整数 2 89 13 | 实现一个函数:求0~100(包括0和100)中为偶数并且恰好是其他数字平方的 14 | 数字 90 15 | 什么是ARC 91 16 | 什么情况下会出现循环引用 92 17 | 说明并比较关键词:strong,weak,assign和copy 93 18 | 说明并比较关键词:atomatic和nonatomic 94 19 | atomic是百分之百线程安全的吗 94 20 | 说明并比较关键词:_ _weak和_ _block 95 21 | 什么是block?它和代理的区别是什么 95 22 | 属性声明代码风格考查 96 23 | 架构解耦代码考查 97 24 | 内存管理语法考查 98 25 | 多线程语法考查 99 26 | 以scheduledTimerWithTimeInterval的方式触发的timer,在滑动页面上的列表时, 27 | timer会暂停,为什么?该如何解决 100 28 | Swift为什么将String,Array和Dictionary设计成值类型 101 29 | 如何用Swift将协议(protocol)中的部分方法设计成可选(optional) 102 30 | 协议的代码实战 103 31 | 在Swift和Objective-C的混合编程项目中,如何在Swift文件中调用 32 | Objective-C文件中定义的方法?如何在Objective-C文件中调用Swift 33 | 文件中定义的方法 104 34 | 比较Swift和Objective-C中的初始化方法(init)有什么异同 105 35 | 比较Swift和Objective-C中的协议(protocol)有什么异同 105 36 | 谈谈对Objective-C和Swift动态特性的理解 105 37 | 语言特性的代码实战 107 38 | message send如果找不到对象,则会如何进行后续处理 108 39 | 什么是method swizzling 108 40 | Swift和Objective-C的自省(Introspection)有什么不同 109 41 | 能否通过Category给已有的类添加属性(property) 110 42 | LLDB中p和po有什么区别 112 43 | Xcode中的Buildtime issues和Runtime issues指什么 113 44 | App启动时间过长,该怎样优化 114 45 | 如何用Xcode检测代码中的循环引用 115 46 | 怎样解决EXC_BAD_ACCESS 116 47 | 如何在Playground中执行异步操作 117 48 | 在playground中实现一个10行的列表,每行随机显示一个0~100的整数 118 49 | 要在UIView上定义一个Label有哪几种方式 119 50 | storyboard/xib和纯代码构建UI相比,有哪些优点和缺点 120 51 | Auto Layout和Frame在UI布局和渲染上有什么区别 121 52 | UIView和CALayer有什么区别 121 53 | 说明并比较关键词:frame,bounds和center 122 54 | 说明并比较方法:layoutIfNeeded,layoutSubviews和setNeedsLayout 123 55 | 说明并比较关键词:Safe Area,SafeAreaLayoutGuide和SafeAreaInsets 123 56 | 在iOS中实现动画的方式有几种 124 57 | 控制屏幕上的圆形小球,使其水平向右滑动200个point 125 58 | 在iOS开发中,如何保证App的UI在iPhone、iPad及iPad分屏情况下 59 | 依然适用 127 60 | 如何用drag & drop实现图片拖动功能 129 61 | 说明并比较关键词:contentView,contentInset,contentSize和contentOffset 131 62 | 说明UITableViewCell的重用机制 133 63 | 说明并比较协议:UITableViewDataSource和UITableViewDelegate 133 64 | 请说明并比较协议:UICollectionViewDataSource,UICollection- ViewDelegate 65 | 和UICollectionViewDelegateFlowLayout 133 66 | 实现一个10行的列表,每行随机显示一个0~100的整数。用户可以删除、 67 | 移动任何一行,下拉列表中的数字重新刷新 134 68 | UICollectionView中的Supplementary Views和Decoration Views分别指什么 137 69 | 如果一个列表视图滑动很慢,那么该怎样优化 138 70 | 说一说实现预加载的方法 139 71 | 如何用UICollectionView实现瀑布流界面 140 72 | 说一说HTTP中GET与POST的区别 142 73 | 说一说Session和Cookie的概念 142 74 | 说明并比较网络通信协议:Ajax Polling,Long Polling,WebSockets和 75 | Sever-Sent Event 143 76 | 在一个HTTPS连接的网站中,输入账号和密码并单击登录按钮后, 77 | 到服务器返回这个请求前,这期间经历了什么 143 78 | 说明并比较类:URLSessionTask,URLSessionDataTask, 79 | URLSessionUploadTask和URLSessionDownloadTask 144 80 | 什么是Completion Handler 145 81 | 设计一个方法,在给定API的网址的条件下,返回用户数据 146 82 | 在iOS开发中,本地消息通知的流程是怎样的 148 83 | 说一说在iOS开发中,远程消息推送的原理 149 84 | 在iOS开发中,如何实现编码和解码 150 85 | 说一说在iOS开发中数据持久化的方案 151 86 | 在iOS开发中,对于并发操作有哪3种方式 153 87 | 比较关键词:Serial,Concurrent,Sync和Async 153 88 | 串行队列的代码实战 154 89 | 并行队列的代码实战 156 90 | 举例说明iOS并发编程中的三大问题 157 91 | 竞态条件的代码实战 160 92 | 试比较GCD中的方法:dispatch_async,dispatch_after,dispatch_ once 93 | 和dispatch_group 162 94 | GCD中全局(global)队列有哪几种优先级 164 95 | 试比较Operations中的关键词:Operation,BlockOperation和OperationQueue 164 96 | 如何在OperationQueue中取消某个Operation 166 97 | 在实际开发中,主线程和其他线程有哪些使用场景 168 98 | 说说你平常开发中用到的设计模式 169 99 | 什么是MVC 170 100 | Objective-C和Swift在单例模式的创建上有什么区别 171 101 | 什么是装饰模式(Decorator) 171 102 | 什么是观察者模式(Observer) 172 103 | 什么是备忘录模式(Memento) 174 104 | 比较苹果官方的MVC架构的优点和缺点 176 105 | MVC架构的代码实战 177 106 | MVCS中的S为什么要单独拆分出来 179 107 | MVP和MVC有什么异同 179 108 | MVVM中的ViewModel的作用是什么 180 109 | 比较MVC、MVP和MVVM这三种架构 181 110 | VIPER之间的各个组件是如何交互的 182 111 | 什么是OOP?它在iOS开发中有哪些优点 184 112 | OOP在iOS开发中有哪些缺点 185 113 | POP相比OOP有哪些优势 187 114 | 要给一个UIButton增加一个点击后抖动的效果,该怎样实现 189 115 | POP的代码实战 190 116 | 试用Swift实现二分搜索算法 192 117 | 一个App崩溃了,可能是什么原因造成的 195 118 | 在模拟机上完成所有测试之后,就不需要在真机上再进行测试了吗 196 119 | 为什么在单元测试中引入代码模块要用@testable关键词 197 120 | 单元测试的代码实战 197 121 | 说一说iOS中的性能测试(performance test) 199 122 | 说一说iOS开发中的UI测试 200 123 | 如何检查测试覆盖率 200 124 | 什么是iOS中的App ID 201 125 | 什么是iOS中的Code Signing 202 126 | 什么是iOS中的App Thinning 202 127 | 向App Store提交App时有哪些原因可能被拒 -------------------------------------------------------------------------------- /iOS/How_to_change_your_Icon_in_code .md: -------------------------------------------------------------------------------- 1 | # iOS通过代码更新应用图标 2 | 3 | 今天iOS 10.3 发布了,新系统为用户带来更好的体验的同时,也为开发者提供了一个新的API可以允许开发者自己指定应用在主屏幕上的图标,虽然还要给用户弹确认,虽然还不能自动定时更新,但谢天谢地总算是能更新了不是吗,终于可以不用再为换个图标而急急忙忙地去审核了。 4 | 5 | 既然有了新功能,那就试试呗,先看看新API: 6 | 7 | ``` Swift 8 | extension UIApplication { 9 | 10 | // If false, alternate icons are not supported for the current process. 11 | @available(iOS 10.3, *) 12 | open var supportsAlternateIcons: Bool { get } 13 | 14 | 15 | // Pass `nil` to use the primary application icon. The completion handler will be invoked asynchronously on an arbitrary background queue; be sure to dispatch back to the main queue before doing any further UI work. 16 | @available(iOS 10.3, *) 17 | open func setAlternateIconName(_ alternateIconName: String?, completionHandler: ((Error?) -> Swift.Void)? = nil) 18 | 19 | 20 | // If `nil`, the primary application icon is being used. 21 | @available(iOS 10.3, *) 22 | open var alternateIconName: String? { get } 23 | } 24 | ``` 25 | 好了,接下来准备两种样式的图标,拖入项目中,然后再写一小段代码: 26 | 27 | ``` Swift 28 | func changeIcon() { 29 | let application = UIApplication.shared 30 | 31 | //首先判断是否设备支持,其次如果已经替换, 32 | if #available(iOS 10.3, *), application.alternateIconName != nil { 33 | //如果已经替换就换回主图标 34 | application.setAlternateIconName(nil, completionHandler: nil); 35 | } else if #available(iOS 10.3, *), application.supportsAlternateIcons { 36 | application.setAlternateIconName("AlternateIcon") { 37 | if let error = $0 { 38 | print(error.localizedDescription) 39 | } else { 40 | print("Change alternate Icon successed") 41 | } 42 | } 43 | } 44 | } 45 | ``` 46 | 运行一下,我们可以看到Console的打印信息提示:“The file doesn’t exist.”。为什么会找不到呢,明明把图标文件添加到项目中了。再回顾下API文档,又这样一段话: 47 | 48 | >The name of the alternate icon, as declared in the CFBundleAlternateIcons key of your app's Info.plist file. Specify nil if you want to display the app's primary icon, which you declare using the CFBundlePrimaryIcon key. Both keys are subentries of the CFBundleIcons key in your app's Info.plist file. 49 | 50 | 意思是我们还要修改一下“Info.plist”文件,在其中添加一些键值。想了解Info.plist文件所有键值可以点[这里](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-SW14) 51 | 52 | 按文档说明添加键值如下: 53 | 54 | ``` xml 55 | CFBundleIcons 56 | 57 | CFBundleAlternateIcons 58 | 59 | CFBundleIconFiles 60 | 61 | AlternateIconFileName 62 | 63 | UIPrerenderedIcon 64 | 65 | 66 | CFBundlePrimaryIcon 67 | 68 | CFBundleIconFiles 69 | 70 | PrimaryIconFileName 71 | 72 | 73 | 74 | ``` 75 | 76 | >注意:如果你的Info.plist文件看上去和我的不一样,请选中它,然后右键“Open as”->"Source code" 77 | 78 | OK,再次运行,还是找不到文件,why?是时候祭出谷歌大法了。终于在苹果官方论坛里找到了[答案](https://forums.developer.apple.com/thread/71463)。看来很多人都在这里被[文档](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-SW14)给误导了。 79 | 80 | 正确的做法是: 81 | 82 | ``` xml 83 | CFBundleIcons 84 | 85 | CFBundleAlternateIcons 86 | 87 | AlternateIconFileName 88 | 89 | CFBundleIconFiles 90 | 91 | AlternateIconFileName 92 | 93 | UIPrerenderedIcon 94 | 95 | 96 | 97 | CFBundlePrimaryIcon 98 | 99 | CFBundleIconFiles 100 | 101 | PrimaryIconFileName 102 | 103 | 104 | 105 | ``` 106 | 可以看出“CFBundleAlternateIcons”字典还需要在嵌一个键与图标文件名相同的字典。 107 | 108 | 修改完成后,在此运行,终于可以更新了。 109 | 110 | ## 参考:[苹果官方论坛 giveme5 的回复](https://forums.developer.apple.com/thread/71463) 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /OSFoundation/Exception_catch.md: -------------------------------------------------------------------------------- 1 | 如果你很清楚操作系统的信号,那么跳过异常这一节。 2 | 3 | ## 异常的来源 4 | 异常控制流(Exceptional Control Flow) 5 | 6 | - 系统层 7 | - 应用层 8 | 9 | 10 | ## 系统层的异常 11 | 异常(Exception),就是控制流中的突变,用来响应处理器状态中的某些变化。 12 | 13 | 示例图 14 | 15 | 当处理器检测到有异常事件发生时,它就通过一个叫做异常表(exception table)的跳转表,进行一个间接过程调用(异常),到一个专门设计用来处理这类事件的操作系统子程序(异常处理程序(exception handler))。当异常处理程序完成处理后,根据引起异常的事件的类型,会发生一下 3 中情况中的一种: 16 | 17 | - 处理程序将控制返回给当前指令 Icurr, 18 | - 处理程序将控制返回给 Inext 19 | - 处理程序终止被中断的程序。即“崩溃” 20 | 21 | ### 异常处理 22 | 23 | ### 异常的类别 24 | 异常可以分为四类:中断、陷阱、故障、终止 25 | 26 | |类别|原因|异步/同步|返回行为| 27 | |:--|:---|:------|:------| 28 | |中断|来自 I/O 设备的信号|异步|总是返回到下一条指令| 29 | |陷阱|有意的异常|同步|总是返回到下一条指令| 30 | |故障|潜在可恢复到错误|同步|可能返回到当前指令| 31 | |终止|不可回复的错误|同步|不会返回| 32 | 33 | **终止** 34 | 35 | 终止是不可恢复的知名错误造成的结果,通常是一些硬件错误,但是某些严重疏忽导致的 Bug 也会终止。终止处理程序从不将控制返回给应用程序,而是讲控制返回给一个 abort 例程,该例程会终止这个应用程序——崩溃。 36 | 37 | ### 进程的终止 38 | 39 | 进程会因为三种原因终止: 40 | 41 | 1. 收到一个信号,该信号的默认行为是终止进程; 42 | 2. 从主程序返回; 43 | 3. 调用 exit 函数; 44 | 45 | ## 信号 46 | 一个信号就是一条小消息,它通知jinching系统中发生了一个某种类型的事件。每种信号类型都对应于某种系统事件。此层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程是不可见的。信号提供了一种机制,通知用户进程发生了这些异常。 47 | 48 | 下图展示了系统定义的不同类型的信号: 49 | 50 | ```c 51 | /* */ 52 | #define SIGHUP 1 /* hangup */ 53 | #define SIGINT 2 /* interrupt */ 54 | #define SIGQUIT 3 /* quit */ 55 | #define SIGILL 4 /* illegal instruction (not reset when caught) */ 56 | #define SIGTRAP 5 /* trace trap (not reset when caught) */ 57 | #define SIGABRT 6 /* abort() */ 58 | #if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE)) 59 | #define SIGPOLL 7 /* pollable event ([XSR] generated, not supported) */ 60 | #else /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */ 61 | #define SIGIOT SIGABRT /* compatibility */ 62 | #define SIGEMT 7 /* EMT instruction */ 63 | #endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */ 64 | #define SIGFPE 8 /* floating point exception */ 65 | #define SIGKILL 9 /* kill (cannot be caught or ignored) */ 66 | #define SIGBUS 10 /* bus error */ 67 | #define SIGSEGV 11 /* segmentation violation */ 68 | #define SIGSYS 12 /* bad argument to system call */ 69 | #define SIGPIPE 13 /* write on a pipe with no one to read it */ 70 | #define SIGALRM 14 /* alarm clock */ 71 | #define SIGTERM 15 /* software termination signal from kill */ 72 | #define SIGURG 16 /* urgent condition on IO channel */ 73 | #define SIGSTOP 17 /* sendable stop signal not from tty */ 74 | #define SIGTSTP 18 /* stop signal from tty */ 75 | #define SIGCONT 19 /* continue a stopped process */ 76 | #define SIGCHLD 20 /* to parent on child stop or exit */ 77 | #define SIGTTIN 21 /* to readers pgrp upon background tty read */ 78 | #define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */ 79 | #if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)) 80 | #define SIGIO 23 /* input/output possible signal */ 81 | #endif 82 | #define SIGXCPU 24 /* exceeded CPU time limit */ 83 | #define SIGXFSZ 25 /* exceeded file size limit */ 84 | #define SIGVTALRM 26 /* virtual time alarm */ 85 | #define SIGPROF 27 /* profiling time alarm */ 86 | #if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)) 87 | #define SIGWINCH 28 /* window size changes */ 88 | #define SIGINFO 29 /* information request */ 89 | #endif 90 | #define SIGUSR1 30 /* user defined signal 1 */ 91 | #define SIGUSR2 31 /* user defined signal 2 */ 92 | ``` 93 | 94 | ### 信号的传递 95 | 一个信号传递到目标进程有两个不同的步骤组成: 96 | 97 | **发送信号**。内核通过更新目的进程上下文中的某个状态,发送一个信号给目的进程。发送信号可以有一下两种原因: 98 | 99 | - 内核检测到一个系统事件,例如除零错误或者子进程终止; 100 | - 一个进程调用了 Kill 函数,显示地要求内核发送一个信号给目的进程。 101 | 102 | **接受信号**。当目的进程被内核强迫以某种方式对信号的发送做出反应时,它距接收了信号。进程可以忽略这个信号,终止或者执行一个称为信号处理程序(signal handler)的用户层函数捕获这个信号。 103 | 104 | ### 接收信号 105 | 当内核把进程 p 从内核模式切换到用户模式时,它会检查进程 p 的未被阻塞的待处理信号的集合。如果这个集合为空,那么内核讲控制传递到 p 的逻辑控制流中的下一条指令。然而,如果集合是非空的,那么内核选择集合中的某个信号 k(通常是最小的 k),并且强制 p 接收信号 k。收到这个信号会触发进程采取某种行为。一旦进程完成了这个行为,那么控制就传递回 p 的逻辑控制流中的下一条指令。每个信号类型都有一个与定义的默认行为: 106 | 107 | - 进程终止 108 | - 进程终止并转储内存 109 | - 进程停止(挂起)直到被 SIGCONT 信号重启 110 | - 进程忽略该信号 111 | 112 | 进程可以通过使用 signal 函数修改和信号相关联的默认行为。唯一的例外是 SIGSTOP 和 SIGKILL,它们的默认行为是不能修改的。 113 | 114 | 那么怎么修改呢?signal 函数可以通过下列三种方法之一来改变和信号 signum 相关联的行为: 115 | 116 | - 如果 handler 是 SIG_IGN,那么忽略类型为 signum 的信号 117 | - 如果 handler 是 SIG_DFL,那么类型为 signum 的信号行为恢复为默认行为 118 | - 否则,handler 就是用户定义的函数的地址,这个函数被称为信号处理程序,只要进程接收到一个类型为 signum 的信号,就会调用这个程序。通过处理程序的地址传递到 signal 函数从而改变默认行为。这被称为信号处理程序(instlalling the handler)。调用信号处理程序被称为捕获信号。执行信号处理程序被称为处理信号。 119 | 120 | 当一个进程捕获来一个类型为 k 的信号时,会调用为信号 k 设置的处理程序,一个蒸熟参数被设置为 k。这个参数允许同一个处理函数捕获不同类型的信号。 121 | 122 | 至此,了解到的这些操作系统知识,已经足够支持我们对系统层的崩溃信号进行捕获了,接下就到了 Coding time。 123 | 124 | ### 捕获崩溃信号 125 | 126 | ```c 127 | 128 | ``` 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_operating_systems.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之操作系统 2 | 3 | ### 1. 堆和栈的区别,为什么要分堆和栈?如何优化?那种会造成内存碎片化? 4 | - 栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等,栈是向低地址扩展的数据结构,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的。 5 | 6 | - 堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时由OS回收,向高地址扩展的数据结构,是不连续的内存区域,从而堆获得的空间比较灵活。 7 | 8 | - 碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出. 9 | 10 | - 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。 11 | 12 | - 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。 13 | 14 | - 全局区(静态区)(static),全局变量和静态变量的存储是放在一块 的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。 15 | 16 | - 文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。 17 | 18 | - 程序代码区—存放函数体的二进制代码 19 | 20 | ### 2. 线程和进程的区别,内存共享、进程买哦树、写时复制、操作系统启动《深入理解计算机系统》 21 | 22 | - 一个程序至少要有进城,一个进程至少要有一个线程. 23 | 进程:资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 24 | - 线程:进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。 25 | - 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。 26 | - 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。 27 | - 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。 28 | 29 | ### 3. 静态常量区访问的过程 30 | 31 | ### 4. 进程间如何通信,方式有哪些? 32 | 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信 33 | 34 | #### 4.1 匿名管道通信(pipe) 35 | 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系(父子进程)的进程间进行。 36 | 37 | 步骤如下: 38 | 39 | - 父进程创建管道,得到2个文件描述符指向管道的两端 40 | - 父进程 fork 出子进程,子进程也有2个文件描述符指向同一个管道 41 | - 父进程关闭 fd[0],子进程关闭 fd[1],因为是单向通信,所以父进程关闭读段,子进程关闭写端。 42 | 43 | #### 4.2 高级管道通信 44 | 高级管道(popen):将另一个程序当作一个新的进程在当前程序进程中启动,则它算作当前程序的子进程,这种方式称为高级管道方式。 45 | 46 | #### 4.3 有名管道通信 47 | 有名管道(named pipe):又名管道也是半双工的通信方式,但它允许无亲缘关系进程间通信。 48 | 49 | #### 4.4 消息队列通信 50 | 消息队列(message queue):消息队列是有消息的链表,存放在内核中并由消息队列标识标识。消息队列克服了信号传递信息少、管道只能承载物格式字节流以及缓冲区大小受限等缺点; 51 | 52 | #### 4.5 信号量通信 53 | 信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止其它进程正在访问资源时,其它进程也访问该资源。因此主要作为进程间以及同一进程不同线程间的同步手段。 54 | 55 | #### 4.6 信号 56 | 信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生 57 | 58 | #### 4.7 共享内存通信 59 | 共享内存(memory shared):共享内存就是映射一段能被其它进程所为访问的内存,这段共享内存由一个进程创建,但可由多个进程访问。共享内存是最快的 IPC 方式,它是针对其它进程间通信运行效率低而设计的。它往往与其它通信机制,如信号量,配合使用,来实现进程间的同步和通信。 60 | 61 | #### 4.8 Socket 62 | 与其它方式不同的是,它可以用于不同主机之间的进程间通信。 63 | 64 | ### 5. 两个进程分别指向同一个地址空间并初始化一个值,分别输出什么? 65 | 66 | ### 6. 程序执行的过程 67 | 68 | 1. 父进程 fork 成一个子进程; 69 | 2. 子进程启动加载器; 70 | 3. 加载器(Dyld)删除子进程现有的虚拟内存段,并创建出一套新的虚拟内存段,也就是代码区、数据区、堆区、共享库区、栈区等; 71 | 4. 通过虚拟地址空间中的页映射到可执行文件到页大小的片(chunk),代码区、数据区初始化为可执行文件的内容; 72 | 5. 通过head取得依赖的共享列表,解析,递归加载有依赖的动态库,Rebasing and binding、 73 | 6. 加载完毕,通知 Objc runtime,运行时开始加载类、扩展并将他们连接起来 74 | 7. C++ generates intializer for staticlly allocated objects 75 | 8. Objc +load methods 76 | 9. Run "bottom up" so each initializer can call dylibs below it 77 | 10. lastly, Dyld calls main() in excutable 78 | 79 | 简化版: 80 | 81 | 1. Parse images 82 | 2. Map images 83 | 3. Rebase images 84 | 4. Bind images 85 | 5. Run image initializers 86 | 6. Call main() 87 | 7. Call UIApplicationMain() 88 | 89 | 测试分析启动过程可以用到 DYLD_PRINT_STATISTICS 环境变量,scheme 的运行中添加。 90 | 91 | 一个应用通常有100到400个动态链接库 92 | 93 | 优化方法: 94 | 95 | 1. 优化加载时间:合并静态、静态库,可以提高加载速度; 96 | 2. Rebase/Binding:缩减 __DATA 指针;缩减 Objc 元数据,如:Clases、selectors、categories;缩减 C++ 虚函数;采用 SWift 结构;严格测试机器自动生成的代码,用偏移量代替指针,标记为只读;总之减少不必要的类,越多越慢; 97 | 3. 运行时:跟2中提到的优化点相同。 98 | 4. Initializers: 尽量用 +initialize 代替 +load;Replace C/C++ `__attribute__((constructor))` with call site initializers。例如:`dispatch_once()`、`pthread_once()`、`std::once()`;或者该用 Swift,就没这些烦恼了;不要在初始化函数中调用 dlopen() 和开辟线程。 99 | 100 | 简化版: 101 | 102 | 1. 减少动态库; 103 | 2. 缩减 Objc 类; 104 | 3. 淘汰静态初始化; 105 | 4. 多用 Swift 106 | 107 | ### 一个进程有哪些区 108 | 109 | 代码区、数据区(只读、可读写)、堆区、共享库、栈区、 110 | 111 | ### 内核态和用户态的区别,为什么要这样分 112 | 为了使操作系统内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。 113 | 114 | ### 为什么要有page cache,操作系统怎么设计的Page cache 115 | 116 | 为什么要设计 page cache,这是因为随着计算的发展,CPU、RAM 和 磁盘之间的速度差异越拉越大,已经到了指数级别,为了提高总体上的运行效率,避免快等慢等问题。才推出了 Page cache。 117 | 118 | 对于系统的所有文件I/O请求,操作系统都是通过page cache机制实现的,对于操作系统而言,磁盘文件都是由一系列的数据块顺序组成,数据块的大小随系统不同而不同,x86 linux系统下是4KB(一个标准页面大小)。内核在处理文件I/O请求时,首先到page cache中查找(page cache中的每一个数据块都设置了文件以及偏移信息),如果未命中,则启动磁盘I/O,将磁盘文件中的数据块加载到page cache中的一个空闲块。之后再copy到用户缓冲区中。 119 | 120 | 参考:《深入理解计算机系统》第9章 虚拟内存 121 | 122 | ### 介绍 5 种IO模型 123 | 124 | - 阻塞 I/O (blocking I/O) 125 | - 非阻塞 I/O (nonblocking I/O) 126 | - I/O 复用 127 | - 信号驱动 I/O 128 | - 异步 I/O 129 | 130 | [5 种网络 I/O 模型](https://www.cnblogs.com/findumars/p/6361627.html) 131 | 132 | ### 异步编程的时间循环 133 | 134 | ### 项目采用64位,为什么用64位,怎么修改成64位,i386又是指什么,他们之间什么关系 135 | 136 | 计算机处理器的位数是由通用寄存器的宽度定义的,一般说的32位和64位指的是处理器的位宽,即通用寄存器的宽度。和32位的处理器相比,64位处理的优势在与能够处理更长的指令,在运行64位指令时效率更高。 137 | 138 | 不需要特别的修改,但需要注意代码中使用的数据类型,尽量使用基于平台的类型,而不是指定的特殊类型。 139 | 140 | i386 指的是 Intel 386,也泛指 IA32 体系的 CPU。在 iOS 开发过程中通常是指针对模拟器编译的包或库。 141 | -------------------------------------------------------------------------------- /OpenCV/OpenCV_base_types.md: -------------------------------------------------------------------------------- 1 | # 【笔记】OpenCV 之基础数据类型 2 | 3 | ## CV::Point 4 | ```cpp 5 | cv::Point2i p; 6 | cv::Point3f p; 7 | cv::Point3f p2( p1 ); 8 | cv::Point2i p( x, y); 9 | cv::Point3d p( x, y, z ); 10 | (cv::Vec3f) p;// 类型投射 11 | p.x; 12 | p.y; 13 | p.z;// 仅限于 3 维点 14 | // 点乘 15 | float x = p1.dot( p2 ); 16 | double x = p1.ddot( p2 ); 17 | // 叉乘 18 | p1.cross( p2 );// 仅限于 3 维点。 19 | p.inside( r );// 判断是否位于矩形内,仅限于 2 维点 20 | ``` 21 | 22 | ## cv::Scalar 23 | ```cpp 24 | cv::Scalar s; 25 | cv::Scalar s2( s1 ); // 通过一个已有的 scalar 来构造 26 | cv::Scalar s( x0 ); 27 | cv::Scalar s( x0, x1, x2, x3 ); 28 | s1.mul( s2 ); // 逐元乘法 29 | //(Quaternion)conjugation: 30 | s1.conj( s2 );// (returns cv::Scalar(s0,-s1,-s2,-s2)) 31 | // (Quaternion)real test: 32 | s1.isReal();// (returns true iff s1==s2==s3==0) 33 | ``` 34 | ## cv::Size 35 | ```cpp 36 | cv::Size sz; 37 | cv::Size2i sz; 38 | cv::Size2f sz; 39 | cv::Size sz2( sz1 ); 40 | cv::Size2f sz( w, h ); 41 | sz.width; sz.height; 42 | 43 | sz.area(); 44 | ``` 45 | 46 | ## cv::Rect 47 | 用来描述那些与坐标轴对齐的矩形 48 | 49 | ```cpp 50 | cv::Rect r; 51 | cv::Rect r2( r1); 52 | cv::Rect( x, y, w, h); 53 | cv::Rect(p, sz); 54 | cv::Rect(p1, p2); // 通过两个点来声明,对角线? 55 | r.x; r.y; r.width; r.height; 56 | r.area(); // 求面积 57 | r.tl(); // top-left 58 | r.br(); // bottom-right 59 | r.contains( p ); // 是否包含点 p 60 | ``` 61 | 重载的一些操作符: 62 | 63 | ```cpp 64 | // 取交叠的矩形 65 | cv::Rect r3 = r1 & r2; 66 | r1 &= r2; 67 | // 68 | cv::Rect r3 = r1 | r2; 69 | r1 |= r2; 70 | // 变换 71 | cv::Rect rx = r + x; 72 | r += x; 73 | // 缩放 74 | cv::Rect rs = r + sz; 75 | r += sz; 76 | // 相等判断 77 | bool eq = (r1 == r2); 78 | // 非等判断 79 | bool ne = (r1 != r2); 80 | ``` 81 | 82 | ## cv::RotatedRect 83 | 用来描述那些坐标轴没对齐的矩形 84 | 85 | ```cpp 86 | cv::RotatedRect rr(); 87 | cv::RotatedRect rr2( rr1 ); 88 | cv::RotatedRect( p1, p2 ); // 通过两个点构建 89 | cv::RotatedRect rr( p, sz, theta ); // 逐元素构建 90 | rr.center; rr.size; rr.angle; // 成员访问 91 | rr.points( pts[4] ); // 返回所有的顶点 92 | ``` 93 | 94 | ## cv::Matx* 95 | 矩阵 96 | 97 | ```cpp 98 | cv::Matx33f m33f; // 3 x 3 float 99 | cv::Mat43f m43d; // 4 x 3 double 100 | 101 | cv::Matx22d m22d( n22d ); // 通过一个已有的矩阵构建一个新矩阵,注意维度必须相同 102 | cv::Matx21f m( x0, x1); // 通过值直接构建一个 2 x 1 的矩阵 103 | // 通过值构建一个 4 x 4 矩阵 104 | cv::Matx44d m(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15); 105 | 106 | m33f = cv::Matx33f::all( x ); // Matrix of identical elements 107 | m23d = cv::Matx23d::zeros(); // 全 0 元素 108 | m16f = cv::Matx16f::ones(); // 全 1 元素 109 | m33f = cv::Matx33f::eye(); // create a unit matrix 110 | 111 | // 构建一个能容纳对角线的矩阵 112 | m31f = cv::Matx33f::diag(); 113 | 114 | // 通过指定元素范围,构建一个随机矩阵 115 | cv::Matx33f::randu( min, max ); 116 | 117 | // Create a matrix with normally distributed entries 118 | cv::Matx33f::nrandn( mean, variance ); 119 | 120 | m( i, j ), m( i ); // 成员访问 121 | 122 | // 矩阵运算 123 | m1 = m0; m0 * m1; m0 + m1; m0 - m1; 124 | m * a; a * m; m / a; 125 | // 比较 126 | m1 == m2; m1 != m2; 127 | 128 | // 点乘 129 | m1.dot( m2 ); 130 | m1.ddot( m2 ); 131 | 132 | // Reshape a matrix 133 | m91f = m33f.reshap<9, 1>(); 134 | 135 | m44f = (Matx44f)m44d;// 投射转换 136 | m44f.get_minor<2, 2>( i, j ); // Extract 2 x 2 submatrix at (i, j) 137 | // Extract row and column 138 | m14f = m44f.row( i ); 139 | m14f = m44f.col( j ); 140 | // Extract matrix diagnoal 141 | m41f = m44f.diag(); 142 | // Compute transpose 143 | n44f = m44f.t(); 144 | // Invert matrix 145 | n44f = m44f.inv( method );// default method is cv::DECOMP_LU 146 | 147 | // Solve linear system 148 | m31f = m33f.solve( rhs31f, method ) 149 | m32f = m33f.solve<2>( rhs32f, method ); default method is DECOMP_LU 150 | 151 | m1.mul( m2);// per-element multiplication 152 | ``` 153 | 154 | ## cv::Vec* 155 | 156 | ```cpp 157 | // 集中构造方法 158 | Vec2s v2s; Vec6f v6f; 159 | Vec3f u3f( v3f ); 160 | Vec2f v2f( x0, x1 ); Vec4d v6d(x0, x1, x2, x3, 4, 5); 161 | // 成员访问 162 | v4f[ i ]; v3w( j ); 163 | // 叉乘 164 | v3f.cross( u3f ); 165 | ``` 166 | 167 | ## cv::Complex* 168 | 169 | ```cpp 170 | // 构造方法 171 | cv::Complexf z1; cv::Complexd z2; 172 | cv::Complexf z2( z1 ); 173 | cv::Complexd z1( re0 ); cv::Complexd( reo, im1 ); 174 | cv::Complexf u2f( v2f ); 175 | 176 | // 成员访问 177 | z1.re; z1.im; 178 | 179 | // Complex conjugate 180 | z2 = z1.conj(); 181 | ``` 182 | 183 | ## 辅助类 184 | 185 | - cv::TermCriteria 186 | - cv::Range 187 | - cv::Ptr 188 | - cv::Exception 189 | - cv::DataType<> 190 | - cv::InputArray & cv::OutputArray 表示所有支持的序列类型, 191 | 192 | ## 实用函数 193 | 194 | - cv::alignPtr() 按指定字节数对齐指针 195 | - cv::alignSize() 按指定字节数对齐 buffer size 196 | - cv::allocate() 创建 C 风格的数组 197 | - cvCeil() 对 float x 向上取整,不会小于 x 198 | - cv::cubeRoot() 199 | - cv::CV_Asser() 200 | - CV_Error() 201 | - CV_Error_() 202 | - cv::deallocate() 203 | - cv::error() 204 | - cv::fastAtan2() 205 | - cv::fastFree() 206 | - cv::fastMalloc() 207 | - cvFloor() 208 | - cv::format() 209 | - cv::getCPUTickCount() 210 | - cv::getNumThreads() 211 | - cv::getOptimalDFTSize() 212 | - cv::getThreadNum() 213 | - cv::getTickCount() 214 | - cv::getTickFrequency() 215 | - cvIsInf() 216 | - cvIsNaN() 217 | - cvRound() 218 | - cv::setNumThreads() 219 | - cv::setUserOptimized() 220 | - cv::useOptimized() 221 | -------------------------------------------------------------------------------- /Ethereum/How_to_build_a_private_blockchain_on_Mac.md: -------------------------------------------------------------------------------- 1 | # Mac环境下搭建以太坊私有链 2 | 3 | 2018年初区块链大火,各种币也层出不穷,但是怎么建立一条自己的私有链呢?接下来我们就动手一步一步的搭建一条自己的私有链。 4 | 5 | 知识储备,阅读本文不需要你具备区块链知识基础,但是如果你对区块链有所了解,那么你就能更好地理解本文中有关创世块的相关设置。此外如果在具备一些 JavaScript 基础,那么你就能更快熟悉有关 Geth 的命令行操作。 6 | 7 | ## 第一步 以太命令行工具安装 8 | 9 | 在以太坊的[官方网站](https://www.ethereum.org)上有详细的[教程](https://www.ethereum.org/cli),所以这里仅简单的列出 Mac 平台的操作命令,其他平台的操作,请参见[官方教程](https://www.ethereum.org/cli) 10 | 11 | 如果你的 Mac 已经安装有Homebrew,那么请在终端中键入如下命令,进行升级更新: 12 | 13 | ``` 14 | brew update 15 | brew upgrade 16 | ``` 17 | 18 | 如果还没有安装 Homebrew,请先按这里的[指引](https://brew.sh)进行安装,然后在终端中键入如下命令: 19 | 20 | ``` 21 | brew tap ehtereum/ethereum 22 | brew install ethereum 23 | ``` 24 | 25 | 注意,这个过程可能很长,尤其是最后检查和编译的时间,以我的 Mac mini 2012 来说,光检查和编译就用了大概45分钟,所以请耐心等待,或者去喝杯咖啡休息休息。 26 | 27 | ## 第二步 建立目录和genesis.json 28 | 29 | 新建一个路径,用于存储区块链相关数据,这里我的路径是: 30 | 31 | ``` 32 | ~/Documents/Ethereum 33 | ``` 34 | 35 | 在该目录中新建一个 `genesis.json` 文件,该文件主要用来配置创世块,具体内容如下: 36 | 37 | ``` JSON 38 | { 39 | "config": { 40 | "chainId": 10, 41 | "homesteadBlock": 0, 42 | "eip155Block": 0, 43 | "eip158Block": 0 44 | }, 45 | "alloc" : {}, 46 | "coinbase" : "0x0000000000000000000000000000000000000000", 47 | "difficulty" : "0x0200000", 48 | "extraData" : "", 49 | "gasLimit" : "0x2fefd8", 50 | "nonce" : "0x0000000000000042", 51 | "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 52 | "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 53 | "timestamp" : "0x00" 54 | } 55 | ``` 56 | 57 | Genesis.json文件的键说明: 58 | 59 | |键|说明| 60 | |:------:|:--------| 61 | | alloc |预置账号,以及初始金额,开局送黄金就是这个啦| 62 | |coinbase|矿工账号,随便填写| 63 | |difficulty|挖矿难度,数值越大,挖矿越耗时,具体控制的是什么,待查清| 64 | |extraData|附加数据,个性信息,具体是哪些个性信息,待查清| 65 | |gasLimit|用于限制GAS的消耗总量,及区块所能包含交易信息的总和| 66 | |nonce|一个64位随机数,用于挖矿。注意该值与mixhash的设置必须满足以太坊黄皮书4.3.4中所述条件| 67 | |mixhash|与nonce配合用于挖矿,由上一区块儿的部分生成的hash,同样需要班组黄皮书中的条件| 68 | |parentHash|上一区块的hash,创世块是没有的,所以为0| 69 | |timstamp|创世块的时间戳| 70 | 71 | 开局送黄金示例: 72 | 73 | ``` JSON 74 | "alloc": { 75 | "0x0000000000000000000000000000000000000001": {"balance": "111111111"}, 76 | "0x0000000000000000000000000000000000000002": {"balance": "222222222"} 77 | } 78 | ``` 79 | 80 | ## 第三步 生成创世块 81 | 82 | 注意我当前目录是 `~/Documents/Ethereum`,且 `genesis.json` 文件恰好在该目录下,区块链相关数据我要放入该目录下的 chain 目录中,所以在终端中键入如下命令: 83 | 84 | ``` Shell 85 | geth --datadir "./chain" init genesis.json 86 | ``` 87 | 88 | 命令执行完毕后,会在 `~/Documents/Ethereum` 目录下新建一个 chain 目录,并在该目录下生成两个新的目录 geth 和 keystore 89 | 两个文件夹分别用于保存如下内容: 90 | 91 | |目录|作用| 92 | |:------:|:--------| 93 | |geth|保存区块链相关数据,如:数据库| 94 | |keystore|保存账户信息| 95 | 96 | ## 第四步 创建私有链 97 | 98 | 在终端中键入如下命令,来启动我们的区块链,并将结果输入到日志 `eth_output.log` 中。注意我这里终端的当前目录是 `~/Documents/Ethereum`,如果你的跟我不同,那么你可能需要指明绝对路径才行。 99 | 100 | ``` 101 | geth --datadir "./chain" --nodiscover console 2>>eth_output.log 102 | ``` 103 | 参数说明: 104 | 105 | - --nodiscover 表示该链不可被发现,即非公开的 106 | - console 进入JavaScript 命令行模式,注意后续如无特别说明,所有命令都是在这里键入的 107 | - 2>>eth_output.log 指定日志文件 108 | 109 | ## 第五步 创建账户 110 | 111 | 通过 `eth.accounts` 命令查看已有账户情况,由于我们在创世块配置文件的 alloc 中没有制定任何信息,所以这里的账户为空,即返回 [] 112 | 113 | 创建账户有两种方式,其实可以算作一种,只是确认密码的方式不同,创建命令如下: 114 | 115 | - `personal.newAccount("123456")` 直接为新账户指定密码,然后返回值即为刚刚创建的账户,例如我的账户: `0x1f5e0c9e14cec895cc95287a425b10d7dc221733` 116 | - `personal.newAccount()` 不指定密码,但是接下来会要求你输入两次密码,之后才返回给你账户。 117 | 118 | ## 第六步 开始挖矿 119 | 120 | `miner.start()` 开始挖矿,挖矿奖励的币会默认保存到第一个创建的账户中。 121 | 122 | 如何查看挖矿的过程呢,还记得我们在第四步中指定的输出日志吗?可以在终端中键入如下命令来查看: 123 | 124 | ``` 125 | tail -f eth_output.log 126 | ``` 127 | 注意我这里终端的当前目录是 `~/Documents/Ethereum`,如果你的跟我不同,那么你需要指明路径才行。 128 | 129 | `miner.stop()` 停止挖矿 130 | 131 | ## 第七部 查询余额与交易 132 | 133 | 获取指定账户的余额,例如,获取我的账户: 134 | 135 | ``` 136 | eth.getBalance("0x1f5e0c9e14cec895cc95287a425b10d7dc221733") 137 | //当然这样也可以 138 | eth.getBalance(eth.accounts[0]) 139 | ``` 140 | 注意,这里余额默认是以最小单位 Wei 来显示的,即一个以太币的1e18分之一。 141 | 142 | 向某个账户发起交易,例如,由账户 A 给账户 B 发送 10 个币: 143 | 144 | ``` 145 | eth.sendTransaction({from:A, to:B, value:web3.toWei(10, "ether")}) 146 | ``` 147 | 如果是首次交易,那么会得到如下错误信息提示: 148 | 149 | ``` 150 | Error: authentication needed: password or unlock 151 | at web3.js:3143:20 152 | at web3.js:6347:15 153 | at web3.js:5081:36 154 | at :1:1 155 | ``` 156 | 我只要需要对账户进行解锁即可: 157 | 158 | ``` 159 | personal.unlockAccount(A, "密码")//注意只解锁花费一方 160 | ``` 161 | 然后再次执行交易命令,即可成功发出交易。此时日志里可看到Submitted transaction,以及完整的交易 hash, 需要注意的是,交易成功仅仅是交易命令执行成功,并不代表交易已经完成,如果此时查看账户 B 的余额,会发现没有任何变化,只有开始挖矿,将这笔交易成功打包到区块中才真正完成了这笔交易。 162 | 163 | ## 更多控制台函数 164 | 更多关于JavaScript VM的操作函数,可以通过在控制台中输入 web3来查看所有,也可以查看各个子模块的函数。 165 | 166 | ## 参考 167 | 1. [Go Ethereum](https://github.com/ethereum/go-ethereum) 168 | 2. [GETH & ETH Command line tools for the Ethereum Network](https://www.ethereum.org/cli) 169 | 3. [使用 Go-Ethereum 1.7.2搭建以太坊私有链](https://mshk.top/2017/11/go-ethereum-1-7-2/) by [迦壹](https://mshk.top/about-me/) 170 | 4. [以太坊执行miner.start返回null](http://blog.csdn.net/wo541075754/article/details/78735711) 171 | 5. [geth配置中,genesis.json的几个问题](http://blog.csdn.net/superswords/article/details/75049323) -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_compiling.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之编译 2 | 3 | ## 1. 编译 4 | 编译是指将程序从源文件转为机器可执行的二进制文件的过程,那么这个过程是什么样的呢?为什么要了解编译的过程呢? 5 | 6 | ### 1.1 编译的过程 7 | 假设我们有现有这样一个源文件 "hello.c": 8 | 9 | ```c 10 | #include 11 | 12 | int main() { 13 | printf("Hello, world!"); 14 | return 0; 15 | } 16 | ``` 17 | 其编译的大致过程如下: 18 | 19 | ```shell 20 | hello.c hello.i hello.s hello.o hello 21 | ---------> 预处理器 -------------> 编译器 ---------> 汇编器 ---------------> 链接器 -------------> 22 | 源程序文件 修改了的源程序 汇编程序 可重定位的目标程序 可执行目标程序 23 | ``` 24 | 25 | 下面简单介绍一下各个步骤: 26 | 27 | 1. 预处理阶段。处理一些符号,例如:#define 的宏、#import,以及其它与处理指令。该过程会输出以 ".i"(C),".ii" (C++), ".mi" (Objective-C), 以及 ".mii" (Objective-C++) 为扩展名的文件。 28 | 2. 编译阶段。编译器将文本文件 hello.i 翻译成文本文件 hello.s,它包含一个汇编语言程序。该阶段还可以被细分为词法分析和语法分析以及代码生成和优化四个子阶段。 29 | 3. 汇编阶段。汇编器将 hello.s 翻译成机器语言指令,把这些指令打包成一种极哦啊做可重定位目标程序等格式,并将结果保存在目标文件 hello.o 中。hello.o 文件是一个二进制文件。 30 | 4. 链接阶段。将目标文件中的符号链接到地址,或者将不能连接到错误反馈给用户。 31 | 32 | 这只是编译的一个大致过程,每一步都有专门的程序负责,因此通常在最开始还会有一个“驱动”,负责将各个子过程串接起来。此外编译阶段和汇编阶段还可以继续细分: 33 | 34 | 1. 词法分析(lexical analysis)。词法分析器读入组成元程序的字符流,并将他们组织成为有意义的词素(lexeme)的序列。对于每个词素,词法分析器产生如下形式的词法单元(token)作为输出:``。 35 | 2. 语法分析(sytax analysis)。语法分析器是用有词法分析器生成的各个词法但愿你的第一个分量来创建属性的中间表示。该中间表示给出来词法分析产生的词法单元流的语法结构。一个常用的白哦时方法是语法树(syntax tree),书中的每个内容结点表示一个运算,而该结点的子结点表示该运算的分量。 36 | 3. 语义分析(semantic analyzer)。使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致。它同时也收集类型信息,并把这些信息存放在语法树或符号表中,以便在会后的中间代码生成过程中使用。 37 | 4. 中间代码生成器。中间表示形式 38 | 5. 机器无关代码优化器。中间表示形式 39 | 6. 代码生成器。目标机器语言 40 | 7. 机器相关代码优化器。目标机器语言 41 | 42 | 参考: 43 | 44 | - 《深入理解计算机系统》第三版 45 | - 《计算机编译原理》 46 | 47 | ### 1.2 介绍一下 Clang 和 LLVM 48 | LLVM(Low Level Virtual Machine)是一个开源的编译器架构。 49 | 50 | Clang (发音为/klæŋ/)是 LLVM 的一个编译器前端,它目前支持 C、C++、Objective-C、Objective-C++。Clang 对原程序进行预处理、词法分析和语义分析(参照上一问题了解全过程),并将分析结果转换为抽象语法树(Abstract Syntax Tree),最后使用 LLVM 作为后端代码的生成器。 51 | 52 | #### 1.2.1 Clang 的开发背景 53 | 由于 GNU 编译器套装 (GCC) 系统庞大,而且 Apple 大量使用的 Objective-C 在 GCC 中优先级较低,同时 GCC 作为一个纯粹的编译系统,与 IDE 配合并不优秀,Apple 决定从零开始写 C family 的前端,也就是基于 LLVM 的 Clang 了。Clang 由 Apple 公司开发,源代码授权使用 BSD 的开源授权 54 | 55 | #### 1.2.2 Clang 的特性 56 | 相比于 GCC,Clang 具有如下优点: 57 | 58 | - 编译速度快:在某些平台上,Clang 的编译速度显著的快过 GCC。 59 | - 占用内存小:Clang 生成的 AST 所占用的内存是 GCC 的五分之一左右。 60 | - 模块化设计:Clang 采用基于库的模块化设计,易于 IDE 集成及其他用途的重用。 61 | - 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告。 62 | - 设计清晰简单,容易理解,易于扩展增强。与代码基础古老的 GCC 相比,学习曲线平缓。 63 | 64 | ### 1.3 编译相关的关键字和宏 65 | #### 1.3.1 static 关键字的作用 66 | 声明为静态,会被编译到静态区,为可见者全局共享。 67 | 68 | ### 1.3.2 define 与 static 的 区别 69 | 70 | - define 定义的内容会在预编译期被预处理器替换掉 71 | - static 会被编译到静态区,且可见者全局共享。 72 | 73 | ### 1.3.3 #import"" 和 #import<> 区别,还有#inlcude 74 | `#import` 和 `#include` 的区别,前者可以避免重复引用,后者则不具备此功能,因此推荐使用前者。 75 | 76 | - `#import ""` 优先搜索本地路径,如果找不到在搜索系统头文件路径检索 77 | - `#import <>` 直接到系统路头文件路径检索 78 | 79 | ## 2. 项目经验 80 | ### 2.1 如何防止别人反编译你的 App? 81 | 除了一些本地数据加密,地址哈希,网络传输加密再没有其它的反编译经验,只能说一些耳闻的内容: 82 | 83 | 1. 本地数据加密。对于存储在手机上的本地数据进行加密处理。如:NSUserDefaults,SQLite 的数据库文件,数据库不存储明文内容,将核心内容存储到 keychain等。 84 | 2. URL 编码加密。对程序中出现的URL进行编码加密,防止URL被静态分析 85 | 3. 网络传输数据加密。对通过网络传输的数据进行加密,如:启用HTTPS,进行双向验证等。 86 | 4. 方法体、方法名高级混淆。对方法名和方法体进行混淆,保证源码被逆向后无法解析代码。 87 | 5. 程序结构混排加密。对应用逻辑结构进行打乱混排,降低逆向后的可读性。 88 | 89 | ### 2.2 静态语言和动态语言的区别 90 | 主要区别就在链接: 91 | 92 | - 静态语言编译后地址都是相对固定的,没有运行期解析的过程,相对更效率 93 | - 动态语言编译后的地址是动态的,要到运行期才能确定,相对更灵活 94 | 95 | ### 2.3 静态库和动态库之间的区别 96 | - 动态库 \*.dylib 和 \*.framework,连接时不复制,程序运行时由系统动态加载到内存, 97 | - 静态库 \*.a 和 \*.framework。连接时,静态会被复制到输出目标中 98 | 99 | ### 2.4 iOS从什么时候开始支持动态库的? 100 | Xcode 6 之后支持创建动态库工程。 101 | 102 | ### 2.5 当引入的三方库发生命名冲突时,怎么解决? 103 | 选择以下方案时,务必注意版本问题。 104 | 105 | 1. 去掉自己工程中的重名源文件,保留头文件。调用三方库中目标; 106 | 2. 通过 ar 工具对三方库进行裁剪,剔除重名的目标文件。 107 | 108 | ## 3 Xcode 109 | 110 | ### 3.1 OtherLinkFlag 111 | 112 | - `-Objc` 加载所有的 Objective-C 类或 Category 113 | - `-all_load` 加载所有目标文件 114 | - `-force_load ` 强制加载指定的文件 115 | 116 | ### 3.2 LLDB 常用调试命令? 117 | LLDB 的命令采用从头匹配的方式,只要没有混淆,就可以执行。 118 | 119 | - help 帮助命令,会列出命令列表,如果后面跟着命令名则会列明详细说明 120 | - print 简化方式:prin、pri、p,不能简化为 pr,因为会和 process 混淆, 121 | - expression 表达式命令,改命令最为强大,几乎所有功能都可以通过它来完成,其它命令几乎都是该命令的简化版 122 | 123 | 大致分类: 124 | 125 | - Execution Commands 126 | - Breakpoint Commands 127 | - Watchpoint Commands 128 | - Examining Variables 129 | - Evaluating Expressons 130 | - Examining Thread State 131 | 132 | > 参考:[《LLDB 8 Documentation》](https://lldb.llvm.org/use/map.html) 133 | 134 | #### 3.2.1 print 135 | 格式:print <表达式> 136 | 137 | 实际上 `print` 是 `expression --` 的简化,会在当前线程执行表达式,并显示返回结果。 138 | 139 | 该命令同样支持格式化输出: 140 | 141 | ```shell 142 | p/x //!< 以十六进制打印整数 143 | p/d //!< 以带符号的十进制打印整数 144 | p/u //!< 以无符号的十进制打印整数 145 | p/o //!< 以八进制打印整数 146 | p/t //!< 以二进制打印整数 147 | p/a //!< 以十六进制打印地址 148 | p/c //!< 打印字符常量 149 | p/f //!< 打印浮点数 150 | p/s //!< 打印字符串 151 | p/r //!< 格式化打印 152 | ``` 153 | 154 | #### 3.2.2 watchpoint 155 | 格式:watchpoint <子命令> <子命令选项> 156 | 157 | 观察点命令,可以对一个地址或某个变量设置观察点,从而可以观察到被观察对象的变化。 158 | 159 | #### 3.2.3 memory read 160 | 格式:memory read 内存地址 161 | 162 | 还可以通过下面的格式设置读取条件: 163 | 164 | memory read/“数量”“格式”“字节数” 内存地址 165 | >注意上面的引号只是表明这三个符号虽然挨着,但是各自有各自的意义,并非命令内容。 166 | 该命令还可以简写为: 167 | 168 | x/“数量”“格式”“字节数” 内存地址 169 | 下面分别对这 3 个限定读取格式的字符进行说明: 170 | 171 | - 数量:自然数 172 | - 格式:“x”表示16进制;“f”表示浮点;“d”表示10进制 173 | - 字节数:"b"表示 byte 即 1 个字节;“h”表示 half word 即 2 个字节;“w”表示 word 即 4 个字节;“g”表示 giant word 即 8 个字节; 174 | 175 | ![memory read](https://user-gold-cdn.xitu.io/2018/4/7/1629daf22085e28b?imageView2/0/w/1280/h/960/ignore-error/1) 176 | -------------------------------------------------------------------------------- /iOS/How_to_decrypt_a_iOS_App.md: -------------------------------------------------------------------------------- 1 | #iOS应用砸壳 2 | 3 | **本人不能,也没有能力保证这里所涉及工具的安全性,请自行辨别。越狱有风险,应用需谨慎** 4 | 5 | ### 为什么要脱壳 6 | 作为一个iOS开发者,经常会看到别人的应用多么多么炫酷,想学习学习吧,它又不是开源的,怎么办呢?只能自己掰开内部瞧一瞧。说是瞧瞧,可真正要打开就没那么简单了,从Appstore下载的App都是经过Apple加密的,也就是俗称的“加壳”,而我们要想一窥究竟,面临的第一个障碍就是,如何去掉这层壳。 7 | 8 | ![pipixia](https://dracarys.github.io/images/pipixia.png) 9 | 10 | 皮皮虾我们去“砸壳”! 11 | 12 | ### 准备工作 13 | 正所谓工欲善其事,必先利其器。没有工具是万万不行的,下面列出了这次“砸壳”过程中所涉及的工具: 14 | 15 | 1. 一部运行 Linux,或者 macOS 的电脑; 16 | 17 | 2. 一部已经越狱的 iOS 设备,并且以安装OpenSSH(在Cydia首页有详细的教程,此处不再赘述),Cycript,avd-cmds; 18 | 19 | 3. 下载主角:[dumpdecrypted](https://github.com/stefanesser/dumpdecrypted) ,并按照Github上的说明进行编译,得到“dumpdecrypted.dylib”。 20 | 21 | 至此所有工具准备就绪,后面会逐步用到它们。 22 | 23 | ### 砸壳流程 24 | 25 | 坚固的堡垒往往容易从内部被攻破,这里也正是利用了这点,将我们得到 dumpdecrypted.dylib 放置到需要砸壳的应用的 Documents 目录下,然后运行从“内部”将其攻破。 26 | 27 | 要实现这一点,我们就要先找到 Documents 目录,然后从桌面端将 dumpdecrypted.dylib 拷贝到手机上,之后在运行之。得到破壳的应用文件后,在从手机端拷贝回桌面端,以供学习研究。 28 | 29 | ### 开始 30 | 31 | 要找到手机上某个应用对应的 Documents 目录,需要先将桌面端与手机端连接起来,你可以通过iExplorer、iTools、iFunBox 等可视化工具操作,也可以直接通过命令行操作,我们这里推荐采用命令行的模式,为什么?因为哪些图形化的工具都要收费,再者命令行操作看上去更酷一些不是吗。 32 | 33 | 是时候请出我们之前准备的OpenSSH了,如果你很感兴趣,可以在这里了解更多关于OpenSSH的内容。首先在检查你的桌面端和手机端是不是在同一网段,例如:这里我的 Mac IP 是192.168.0.109,手机 IP 是192.168.0.129。确定后打开终端(Terminal),输入如下命令并执行: 34 | 35 | ``` Shell 36 | ssh root@192.168.0.129 37 | ``` 38 | 执行后会要求你输入登录密码,注意如果你在手机上安装 OpenSSH 时修改过密码,那么这里请输入新密码,如果没有,那么默认的密码是:“alpine”。 39 | 输入密码后后就远程登录到了手机端,很酷是不是? 40 | 41 | 登录之后要做什么呢?当然是找“堡垒”的位置,以便安插我们的“间谍”--dumpdecrypted.dylib啊,可目录众多怎么找呢?别慌,有办法。首先打开那个我们要借鉴学习的应用,然后把它应用全部关闭,保证只有我们选中的那个应用处于活动状态(在后台也可以。 42 | 43 | 接下来在终端中输入如下命令: 44 | 45 | ``` Shell 46 | ps -e | grep var 47 | ``` 48 | 之后可以在终端上看到: 49 | 50 | ``` Shell 51 | 52 | 127 ?? 0:00.89 /usr/libexec/pkd -d/var/db/PlugInKit-Annotations 53 | 1270 ?? 0:00.23 /private/var/db/stash/_.r4WV27/Applications/MobileSafari.app/webbookmarksd 54 | 1545 ?? 0:05.46 /var/mobile/Containers/Bundle/Application/58E4482C-E4C1-413C-85C5-BBE9AE34E5A1/target.app/target 55 | 1598 ttys000 0:00.01 grep var 56 | ``` 57 | 58 | 这里的 “target” 就是我们要学习研究的应用。注意这里如果提示不识别 ps 命令,请重新安装 avd-cmds 工具。 59 | 60 | 接下来就要祭出大杀器 Cycript 的时候了,在终端输入: 61 | 62 | 63 | ```Shell 64 | cycript -p /var/mobile/Containers/Bundle/Application/58E4482C-E4C1-413C-85C5-BBE9AE34E5A1/target.app/target 65 | ``` 66 | 67 | 执行后如果出现 `cy#` 提示符,就说明我们已经成功进入 Cycript 命令操作状态,Cycript可以做的事情很多,感兴趣的可以参考这篇[文章](http://www.liuchendi.com/2015/12/19/iOS/23_Cycrip/)。 68 | 69 | 接下来在终端输入如下命令: 70 | 71 | ``` Shell 72 | [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0] 73 | ``` 74 | 如果你熟悉 iOS 开发,是不是感觉很眼熟,这不就是我们平时获取 Documents 目录的方法吗,是的。这也正是 Cycript 的魅力。 75 | 输入命令后就会显示出对应的Documents目录了,记下目录,按 `Control+D` 退出Cycipt。 76 | 77 | 既然已经准确地定位到 Documents (堡垒)目录,那么接下来就是放“间谍”进去啦。 78 | 79 | 这里你可以通过之前介绍的几个图形化工具,将 dumpdecrypted.dylib 直接拷贝进去,也可以像我一样用命令操作。 80 | 退出SSH连接,怎么退?`logout` 啊。在终端上进入我们之前准备好的dumpdecrypted.dylib 所在目录。然后在终端上输入如下命令: 81 | 82 | ``` Shell 83 | scp dumpdecrypted.dylib root@192.168.0.129:/var/mobile/Containers/Data/Application/9C376783-E2FA-4B5C-8167-538D5C2FE31A/Documents/ 84 | ``` 85 | 执行后,会要求你输入密码,输入正确的密码后,即开始向目标目录拷贝。这里你可以看到拷贝的进度。 86 | 87 | 拷贝完成后,我们再次通过 SSH 连接到手机端,进入目标应用的 Documents 目录,先 `ls`一下看看我们的文件是否拷贝正确。没问题,就在终端中输入如下命令: 88 | 89 | ``` Shell 90 | DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Containers/Bundle/Application/58E4482C-E4C1-413C-85C5-BBE9AE34E5A1/target.app/target 91 | ``` 92 | 注意上面的路径,要正确,是待砸壳 App 所在路径,不是 Documents 的路径。 93 | 94 | 执行完毕后,我们在 `ls` 一下,看看是不是多了一个XXX.decrypted的文件,有就说明砸壳成功啦。 95 | 96 | 接下来就是怎么把它拷贝到我们的电脑上,以便研究学习了。怎么办呢,还是通过scp命令。 97 | 98 | 退出SSH连接,回到我们的桌面系统,在终端上输入如下命令: 99 | 100 | ``` Shell 101 | scp root@192.168.0.129:/var/mobile/Containers/Data/Application/9C376783-E2FA-4B5C-8167-538D5C2FE31A/Documents/wifikey.decrypted /Users/hahaha/Documents/cracktools/ 102 | ``` 103 | 同样,在拷贝进度完成后,我们就可以在 `Users/hahaha/Documents/cracktools`目录下找到我们的 “target.decrypted” 文件了。至此砸壳全部完成。 104 | 105 | 106 | ### 导出头文件 107 | 108 | 仅仅止步于砸壳是没有意义的,重点是我们要学习它内部是怎么组织的,那么如果能看到目标应用的所有头文件是不是很有帮助呢,答案自然不言而喻。接下来就从我们刚刚砸完壳的文件中把头文件导出来。 109 | 110 | 这里就用到 class-dump 这件法宝了, [github地址](https://github.com/nygard/class-dump) 111 | 112 | 在终端上进入 target.decrypted 所在目录,例如我这里是Users/hahaha/Documents/cracktools/,然后在终端上执行如下命令: 113 | 114 | ``` Shell 115 | class-dump -H wifikey.decrypted -o outputHeaders 116 | ``` 117 | 如果提示找不到class-dump命令,请检查是否拷贝到了 `/usr/local/bin`目录。 118 | 119 | 进入 outputHeaders 看看,如果你和我一样,用的是越狱 iPhone 4s,那么将会是空的,这是因为我们没有指定正确的指令集。下面是iOS对应的指令集: 120 | 121 | |指令集|设备| 122 | |:---|:---| 123 | |armv6|iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch| 124 | |armv7|iPhone3GS, iPhone4, iPhone4S, iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4| 125 | |armv7s| iPhone5, iPhone5C, iPad4(iPad with Retina Display)| 126 | |arm64| iPhone5S, iPad Air, iPad mini2(iPad mini with Retina Display)| 127 | 128 | 接下来我们通过指定指定指令集在试一次: 129 | 130 | ``` Shell 131 | class-dump --arch armv7 -H wifikey.decrypted -o outputHeaders 132 | ``` 133 | 看看 outputHeaders 文件夹,嗯,所有的 头文件都被dump出来了。 134 | 135 | ### 后记 136 | 其实iOS砸壳的教程已经很多了,但是今天本人实际操作下来,还是不是很顺利,别人的文章都不是少说了这里,就是少说了那里,不过好在最后自己尝试成功了。为了避免后人像我一样浪费时间,还是在写一篇的好。当然了,也不排除是本人自己笨,理解能力不强,不管怎么样,就算是重复,至少也能增强自己的记忆不是吗。这就够了。 137 | 138 | ### 参考 139 | [《iOS逆向 - dumpdecrypted工具砸壳》by 小木头](http://www.liuchendi.com/2015/12/23/iOS/24_dumpdecrypted/) 140 | 141 | [《iOS逆向 - Cycript基本用法》 by 小木头](http://www.liuchendi.com/2015/12/19/iOS/23_Cycrip/) 142 | 143 | [《iOS逆向之App脱壳》 by 龙马君](http://www.jianshu.com/p/47836c78eb0a) 144 | 145 | [《iOS逆向工程之App脱壳》 by 青玉伏案](http://www.cnblogs.com/ludashi/p/5725743.html) 146 | 147 | -------------------------------------------------------------------------------- /Ethereum/Layout_of_a_solidity_source_file.md: -------------------------------------------------------------------------------- 1 | # 【译】Solidity源文件结构 2 | 3 | 源文件中可以定义任意数量的合约,及若干 include 和 pragma。 4 | 5 | ## 版本标注(Version Pragma) 6 | 7 | 源文件中可以(也应该)使用版本标注来指明版本,一面将来更新版本的编辑器在编译器时发生异常。 We try to keep such changes to an absolute minimum and especially introduce changes in a way that changes in semantics will also require changes in the syntax, but this is of course not always possible. Because of that, it is always a good idea to read through the changelog at least for releases that contain breaking changes, those releases will always have versions of the form `0.x.0` 或者 `x.0.0`。 8 | 9 | 版本标注的使用方法如下: 10 | 11 | ```Solidity 12 | pragma solidity ^0.4.19; 13 | ``` 14 | 如此该源文件将无法被低于0.4.0的编译器编译,同时它也将不能在高于0.5.0的编译器上工作(该限制是通过`^`来指定)。如此一来在0.5.0版本之前都不会有什么大的变动,以保证代码按照我们的预先设想被编译。大版本虽然不变,但小版本的bug修复还是会持续不断的。 15 | 16 | 除此之外,还可以为编译器版本制定更多规则,详细方法请参见 [npm](https://docs.npmjs.com/misc/semver) 的使用。 17 | 18 | ## 引用其它源文件(Importing other Source Files) 19 | 20 | ### 语法和语义(Syntax and Semantics) 21 | 22 | Solidity与 JavaScritp ES6 类似支持 import声明,但 Solidity 没有 “default export”的概念。 23 | 24 | 在全局作用范围内,可以通过一下方式进行 import声明: 25 | 26 | ```Solidity 27 | import "filename"; 28 | ``` 29 | 30 | 下面的声明会将“filename”中的所有全局符号引入到当前作用范围内。 31 | 32 | ```Solidity 33 | import * as symbolName from "filename"; 34 | ``` 35 | ...同时创建一个包涵 `filename` 文件中所有全局符号的新的全局符号 `symbolName` 。 36 | 37 | ```Solidity 38 | import {symbol1 as alias, symbol2} from "filename"; 39 | ``` 40 | 41 | ...创建2个新的全局符号`alias`和`symbol2`,他们分别引用自`filename`文件中的`symbol1`和`symbol2`。 42 | 43 | 另一种语法虽然ES6种没有,但是相当便利: 44 | 45 | ```Solidity 46 | import "filename" as symbolName; 47 | ``` 48 | 它等同于`import * as symbolName from "filename";` 49 | 50 | ### 路径(Paths) 51 | 52 | 如前文所示,`filename`总是被当作以`/`作为分割符的路径来处理,`.`表示当前目录,`..`表示上一级目录。一旦`.`或`..`后面跟一个`/`以外的字符,它将不再表示当前或者上一级目录。除非以`.`或者`..`开头,否则所有路径都将视为绝对路径。 53 | 54 | 可以这样`import "./x" as x;`来引入统计目录中的`x`文件。如果你这样写`import "x" as x;`,那么将有可能引入其它同名文件(例如位于“include”目录的同名文件)。 55 | 56 | 如何解析路径完全依赖于编译器。通常,在本地的文件系统中你不需要严格地表示出完整的路径层级关系,此外它还支持发现:ipfs,http以及git。 57 | 58 | ### (Use in Actual Compilers) 59 | When the compiler is invoked, it is not only possible to specify how to discover the first element of a path, but it is possible to specify path prefix remappings so that e.g. `github.com/ethereum/dapp-bin/library` is remapped to `/usr/local/dapp-bin/library` and the compiler will read the files from there. If multiple remappings can be applied, the one with the longest key is tried first. This allows for a “fallback-remapping” with e.g. "" maps to `"/usr/local/include/solidity"`. Furthermore, these remappings can depend on the context, which allows you to configure packages to import e.g. different versions of a library of the same name. 60 | 61 | #### solc: 62 | 63 | For solc (the commandline compiler), these remappings are provided as `context:prefix=target` arguments, where both the `context: `and the `=target` parts are optional (where target defaults to prefix in that case). All remapping values that are regular files are compiled (including their dependencies). This mechanism is completely backwards-compatible (as long as no filename contains = or :) and thus not a breaking change. All imports in files in or below the directory `context` that import a file that starts with prefix are redirected by replacing `prefix` by `target`. 64 | 65 | So as an example, if you clone `github.com/ethereum/dapp-bin/` locally to `/usr/local/dapp-bin`, you can use the following in your source file: 66 | 67 | ``` 68 | import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping; 69 | ``` 70 | 71 | and then run the compiler as 72 | 73 | ``` 74 | solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol 75 | ``` 76 | 77 | As a more complex example, suppose you rely on some module that uses a very old version of dapp-bin. That old version of dapp-bin is checked out at /usr/local/dapp-bin_old, then you can use 78 | 79 | ``` 80 | solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \ 81 | module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \ 82 | source.sol 83 | ``` 84 | 85 | so that all imports in `module2` point to the old version but imports in `module1` get the new version. 86 | 87 | Note that solc only allows you to include files from certain directories: They have to be in the directory (or subdirectory) of one of the explicitly specified source files or in the directory (or subdirectory) of a remapping target. If you want to allow direct absolute includes, just add the remapping `=/`. 88 | 89 | If there are multiple remappings that lead to a valid file, the remapping with the longest common prefix is chosen. 90 | 91 | #### Remix: 92 | 93 | *Remix* provides an automatic remapping for github and will also automatically retrieve the file over the network: You can import the iterable mapping by e.g. 94 | 95 | ``` 96 | import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping; 97 | ``` 98 | 99 | Other source code providers may be added in the future. 100 | 101 | ## 注释(Comments) 102 | 103 | 单行注释`//`和多行注释`/*...*/`都是允许的。 104 | 105 | ```Solidity 106 | // 这是一个单行注释 107 | 108 | /* 109 | 这是一个 110 | 多行注释 111 | */ 112 | ``` 113 | 此外,还有一种注释被称为natspec注释,关于它的文档尚未完成。该注释以三个斜杠`///`或者双星号`/**...*/`开始,该注释应该直接应用在函数声明前。可以在注释中插入Doxygen标记,以便用户更好的理解该函数。 114 | 115 | 在下面的例子中,我们通过注释说明了智能合约的title以及2个输出参数和2个返回值。 116 | 117 | ``` Solidity 118 | pragma solidity ^0.4.0; 119 | 120 | /** @title Shape calculator. */ 121 | contract shapeCalculator { 122 | /** @dev 通过计算得到矩形的面积和周长。 123 | * @param w 矩形的宽度。 124 | * @param h 矩形的高度。 125 | * @return s 计算得到的面积。 126 | * @return p 计算得到的周长。 127 | */ 128 | function rectangle(uint w, uint h) returns (uint s, uint p) { 129 | s = w * h; 130 | p = 2 * (w + h); 131 | } 132 | } 133 | ``` -------------------------------------------------------------------------------- /iOS/What_is_a_meta-class_in_Objective-C.md: -------------------------------------------------------------------------------- 1 | # \[译]Objective-C 中的元类(meta-class) 2 | 3 | *本文翻译自 [What is a meta-class in Objective-C](https://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html)*, *由 Matt Gallagher 发表于 [cocoawithlove](https://www.cocoawithlove.com)*。 4 | 5 | *受限于译者英语水平及翻译经验,译文难免有词不达意,甚至错误的地方,还望不吝赐教,予以指正* 6 | 7 | ------------------- 8 | 9 | 本文我们将剖析一个在 Objective-C 中比较陌生的概念——元类(meta-class)。Objective-C 中的每个类都有和自己相关联的元类,但你可能从来没有直接使用过它,它始终罩着一层神秘的面纱。为了一探究竟我们首先看看怎么在运时(runtime)创建一个类。然后透过创建的“class pair”(这里的pair做成双理解),我会解释什么是元类,然后探讨它对于 Objective-C 中对象和类的意义。 10 | 11 | ### 在运行时创建一个类 12 | 13 | 下面的代码在运行时创建了一个 NSError 的子类,并且给它添加了一个方法: 14 | 15 | ``` Objective-C 16 | Class newClass = objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0); 17 | class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:"); 18 | objc_registerClassPair(newClass); 19 | ``` 20 | 21 | 上面添加的方法,以`ReportFunction`函数作为它的实现,具体定义如下: 22 | 23 | ``` Objective-C 24 | void ReportFunction(id self, SEL _cmd) 25 | { 26 | NSLog(@"This object is %p.", self); 27 | NSLog(@"Class is %@, and super is %@.", [self class], [self superclass]); 28 | 29 | Class currentClass = [self class]; 30 | for (int i = 1; i < 5; i++) 31 | { 32 | NSLog(@"Following the isa pointer %d times gives %p", i, currentClass); 33 | currentClass = object_getClass(currentClass); 34 | } 35 | 36 | NSLog(@"NSObject's class is %p", [NSObject class]); 37 | NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class])); 38 | } 39 | ``` 40 | 41 | 表面上看来,这相当简单。在运行时创建一个类只需要简单三步: 42 | 43 | 1. 为”class pair”分配内存 (使用`objc_allocateClassPair`); 44 | 2. 添加所学的方法和变量到类中 (我已经通过 class_addMethod 添加了一个方法); 45 | 3. 注册类以便它能使用 (使用`objc_registerClassPair`) 46 | 47 | 然而,随之而来的问题是:“class pair”是个什么鬼啊?`objc_allocateClassPair`函数怎么就返回了一个值:the class。不是pair(成对的)吗,说好的另一半呢? 48 | 49 | 我相信你已经猜到了,另一半就是元类(也就是本文的主题)。为了解释它是什么和我们为什么需要它,还需要交代下 Objective-C 的对象和类的相关背景。 50 | 51 | ### 对一个数据结构而言到底怎么才能称之为对象呢? 52 | 53 | 每个对象都有类。这是面向对象的基本概念,在Objective-C中,它对数据结构也一样。任何含有一个指向其准确类地址的指针的数据结构,都可以被视作为对象。 54 | 55 | 在 Objective-C 中,对象的类是`isa`指针决定的.`isa`指针指向对象所属的类。 56 | 57 | 实际上,Objective-C 中对象最基本的定义是这样的: 58 | 59 | ``` Ojbective-C 60 | typedef struct objc_object { 61 | Class isa; 62 | } *id; 63 | ``` 64 | 65 | 也就是说:任何一个以指向`Class`结构的指针为开始的结构体都可以被视作`objc_object`。 66 | 67 | Objective-C 中对象最重要的特点就是,你可以发送消息给它们: 68 | 69 | ``` Objective-C 70 | [@"stringValue" writeToFile:@"/file.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL]; 71 | ``` 72 | 73 | 之所以能发送成功是因为 Objective-C 对象(例如上面的`NSCFString`)在发送消息时,是因为运行时可以沿着对象的`isa`指针找到其所属的类(这里是`NSCFString`类)。该类包含一个可以适用所有该类实例对象的方法列表,和一个指向`父类(superclass)`的指针。运行时通过检查这个方法类标以及通过指针检查其父类的方法列表,从而找到一个匹配这条消息的方法(在上面的代码里,是`NSString`类的`writeToFile:atomically:encoding:error`方法)。之后运行时会调用相应的实现(`IMP`)。 74 | 75 | 关键点就在于`Class`定义了你可以给一个对象发送哪些方法。 76 | 77 | ### 什么是元类(meta-class)? 78 | 79 | 现在,可能你已经知道了,Objective-C 的一个类也是一个对象。这意味着你可以发送消息给一个类。 80 | 81 | ``` Objective-C 82 | NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding]; 83 | ``` 84 | 85 | 在这个示例里,`defaultStringEncoding`被发送给了`NSString`类。 86 | 87 | 之所以能成功是因为 Objective-C 中每个类本身也是一个对象。如上面所看到的,这意味着类结构也必须以一个isa指针开始,从而可以和`objc_object`在二进制层面兼容,之后这个结构的下一字段必须是一个指向父类的指针(对于基类则为nil)。 88 | 89 | 正如我上周展示的,定义一个`Class`有很多种方式,取决于你的运行时库版本,但有一点,它们都以`isa`字段开始,并且仅跟着一个`superclass`字段。 90 | 91 | ``` Objective-C 92 | typedef struct objc_class *Class; 93 | struct objc_class { 94 | Class isa; 95 | Class super_class; 96 | /* followed by runtime specific details... */ 97 | }; 98 | ``` 99 | 100 | 为了调用`Class`里的方法,该`Class`的`isa`指针也必须指向一个包含了该`Class`方法列表的`Class`。 101 | 102 | 这就引出了元类的定义:元类是`Class`的类。 103 | 104 | 简单来说就是: 105 | - 当你给对象发送消息时,消息是在寻找这个对象的类的方法列表; 106 | - 当你给类发消息时,消息是在寻找这个类的元类的方法列表。 107 | 108 | 元类是必不可少的,因为它存储了类的类方法。每个类都必须有独一无二的元类,因为每个类都有独一无二的类方法。 109 | 110 | ### 元类的类是什么? 111 | 112 | 元类,就像之前的类一样,它也是一个对象。你也可以调用它的方法。自然的,这就意味着他必须也有一个类。 113 | 114 | 所有的元类都使用根元类(继承体系中处于顶端的类的元类)作为他们的类。这就意味着所有`NSObject`的子类(大多数类)的元类都会以`NSObject`的元类作为他们的类 115 | 116 | 根据这个规则,所有的元类使用根元类作为他们的类,根元类的元类则就是它自己。也就是说基类的元类的isa指针指向他自己。 117 | 118 | ### 类和元类的继承 119 | 120 | 类用`super_class`指针指向了父类,同样的,元类用`super_class`指向类的`super_class`的元类。 121 | 122 | 说的更拗口一点就是,根元类把它自己的基类设置成了`super_class`。 123 | 124 | 在这样的继承体系下,所有实例、类以及元类都继承自一个基类。 125 | 126 | 这意味着对于继承于`NSObject`的所有实例、类和元类,他们可以使用`NSObject`的所有实例方法,类和元类可以使用NSObject的所有类方法 127 | 128 | 这些文字看起来莫名其妙难以理解。[Greg Parker](http://www.sealiesoftware.com/blog/)给出了一份精彩的[关系图](http://www.sealiesoftware.com/blog/class%20diagram.pdf): 129 | 130 | ### 验证实验 131 | 132 | 为了验证,让我们看看我在文章开始写的`ReportFunction`函数的输出。这个函数的目的是跟随`isa`指针并打印出它的路途。 133 | 134 | 为了运行`ReportFunction`,我们需要创建一个动态实例来创建类调用`report`方法。 135 | 136 | ``` Objective-C 137 | id instanceOfNewClass = [[newClass alloc] initWithDomain:@"someDomain" code:0 userInfo:nil]; 138 | [instanceOfNewClass performSelector:@selector(report)]; 139 | [instanceOfNewClass release]; 140 | ``` 141 | 142 | 这里没有声明`report`方法,但我使用`performSelector:`调用它,所以编译器不会给出警告。 143 | 然后`ReportFunction`函数会沿着isa进行检索,来告诉我们class,meta-class以及meta-class的class是什么样的情况: 144 | 145 | ``` 146 | 得到对象的类:ReportFunction 函数使用object_getClass跟踪isa指针,因为isa指针是类的保护成员(你不能直接接收其他对象的isa指针)。ReportFunction不使用类方法,因为在类对象里调用类方法不能返回元类,它会再次返回这个类(因此[NSString class]会返回NSString 类而不是NSString元类) 147 | ``` 148 | 这是程序运行时的输出(省略了`NSlog`前缀): 149 | 150 | ``` Objective-C 151 | This object is 0x10010c810. 152 | Class is RuntimeErrorSubclass, and super is NSError. 153 | Following the isa pointer 1 times gives 0x10010c600 154 | Following the isa pointer 2 times gives 0x10010c630 155 | Following the isa pointer 3 times gives 0x7fff71038480 156 | Following the isa pointer 4 times gives 0x7fff71038480 157 | NSObject's class is 0x7fff710384a8 158 | NSObject's meta class is 0x7fff71038480 159 | ``` 160 | 161 | 观察`isa`到达过的地址的值: 162 | 163 | - 对象的地址是 `0x10010c810` 164 | - 类的地址是`0x10010c600` 165 | - 元类的地址是`0x10010c630` 166 | - 根元类(`NSObject`的元类)的地址是`0x7fff71038480` 167 | - `NSObject`元类的类是它本身 168 | 169 | 这些地址的值并不重要,重要的是它们说明了文中讨论的从类到元类到`NSObject`的元类的整个流程。 170 | 171 | ### 最后 172 | 173 | 元类是`Class`的类。每个`Class`都有自己独一无二的元类(每个类都有自己独一无二的方法列表)。这意味着所有的类对象都不同。 174 | 175 | 元类总是会确保类对象和基类的所有实例和类方法。对于从`NSObject`继承下来的类,这意味着所有的`NSObject`实例和`protocol`方法在所有的类(和meta-class)中都可以使用。 176 | 177 | 所有元类都用基类作为自己的类,对于顶层基类的元类也是如此,只是它指向自己而已(译者注:请参照文中引用的关系图,一目了然)。 178 | -------------------------------------------------------------------------------- /Python/Flask/Flask_notes_01.md: -------------------------------------------------------------------------------- 1 | # 【整理】Flask 学习笔记之一 2 | 3 | 这是学习 flask 的第一篇笔记,重在快速概览,了解上手的一些基础知识。为什么这些文章里都有的内容还要在打一份出来呢?老话讲“好记性不如烂笔头”,主要是为了避免注意力分散,加深记忆。 4 | 5 | ### 1、如何启动 falsk 6 | 7 | ``` Shell 8 | $ export FLASK_APP=yourfile.py 9 | $ flask run 10 | ``` 11 | 或者 12 | 13 | ``` Shell 14 | $ export FLASK_APP=yourfile.py 15 | $ python -m flask run 16 | ``` 17 | 18 | 如何让其它机器也访问到呢?指定监听所有 IP 地址即可。 19 | 20 | flask run --host=0.0.0.0 21 | 22 | 启动调试模式 23 | 24 | $ export FLASK_ENV=development 25 | 26 | 27 | 28 | ### 2、路由参数类型转换 29 | 30 | `@app.route('/path/')` 的转换类型 31 | 32 | |类型|说明| 33 | |:-----|:-------------| 34 | |string| 接受任何不包含斜杠的文本(默认值)| 35 | |int | 接受正整数| 36 | |float | 接受正浮点数| 37 | |path | 类似 string ,但可以包含斜杠| 38 | |uuid | 接受 UUID 字符串| 39 | 40 | ### 3、唯一的 URL/ 重定向行为 41 | 42 | `@app.route('/projects/')` 和 `@app.route('/about')` 只有一个斜杠的区别,前者如果在地址中未加斜杠,flask 也会帮你重定向到正确位置,但后者如果加了斜杠则会 404 错误! 43 | 44 | ### 4、URL构建 45 | `url_for()`函数用于构建指定函数的URL。为什么用它呢? 46 | 47 | - 翻转通常比硬编码URL的表述性更好 48 | - 便于集中处理,减少散落 49 | - URL创建会处理特殊字符的转移和 Unicode 数据 50 | - 生产的路径总是绝对路径,可以避免相对路径产生副作用 51 | - 即是应用不在URL根路径,那么它也会妥善处理 52 | 53 | ### 5、响应不同的HTTP方法 54 | 55 | 通过装饰器的 Methods 参数来指定响应的方法,例如: 56 | 57 | ``` python 58 | @app.route('/login', methods=['GET', 'POST']) 59 | def login(): 60 | if request.method == 'POST' 61 | return your_post() 62 | else: 63 | return your_get() 64 | ``` 65 | 66 | ### 6、渲染模板 67 | 68 | 这里 flask 跟 Django 一样,都采用了模板的方式,免去用户自己转义的麻烦。但是 Django 的模板引擎是自己的(这个没有深入验证),而 flask 的模板引擎采用的是 jinja2,提供更丰富的功能(正在学)。一个简单例子: 69 | 70 | ``` python 71 | from flask import render_template 72 | 73 | @app.route('/hello/') 74 | @app.route('/hello/') 75 | def hello(name=None): 76 | return render_template('hello.html', name=name) 77 | ``` 78 | Flask 会在 templates 文件夹内寻找模板。因此,如果你的应用是一个模块, 那么模板文件夹应该在模块旁边;如果是一个包,那么就应该在包里面: 79 | 80 | 情形 1 : 一个模块: 81 | 82 | ``` Shell 83 | /application.py 84 | /templates 85 | /hello.html 86 | ``` 87 | 情形 2 : 一个包: 88 | 89 | ``` Shell 90 | /application 91 | /__init__.py 92 | /templates 93 | /hello.html 94 | ``` 95 | 96 | ### 7、请求对象 97 | 98 | 通过 `method` 属性可以操作当前请求方法,通过使用 `form` 属性处理(在 POST或者PUT请求中传输的)表单数据 99 | 100 | 例如: 101 | 102 | ``` python 103 | from flask import Flask 104 | from flask import request 105 | from flask import render_template 106 | 107 | @app.route('/login', methods=['POST', 'GET']) 108 | def login(): 109 | error = None 110 | if request.method == 'POST': 111 | if valid_login(request.form['username'], request.form['password']): 112 | return log_the_user_in(request.form['username']) 113 | else: 114 | error = 'Invalid username/password' 115 | return render_template('login.html', error=error) 116 | ``` 117 | 118 | 获取 GET 方法中的参数: 119 | 120 | searchword = request.args.get('key', '') 121 | 122 | 与上面会自动生成一个 HTTP 400 Bad Request 不同,这里如果出现 *keyError* 错误,需要自行捕捉以提升用户体验。 123 | 124 | ### 8、文件上传 125 | 126 | 上传文件无比要在HTML表单中设置 `enctype="multipart/form-data"` 属性,浏览器不会上传。 127 | 128 | ``` Python 129 | from flask import request 130 | from werkzeug.utils import secure_filename 131 | 132 | @app.route('/upload', methods=['POST', 'GET']) 133 | def upload_file(): 134 | if request.method == 'POST': 135 | file = request.files['the_file'] 136 | f.save('/var/www/uploads/' + secure_filename(f.filename)) 137 | ``` 138 | 上传完毕的文件被存储在内存或文件系统的临时位置,需要自行保存。 139 | 140 | ### 9、Cookies 141 | 142 | 读取: 143 | 144 | ``` Python 145 | from flask import request 146 | 147 | @app.route('/') 148 | def index(): 149 | username = request.cookies.get('username') 150 | # 通过get取值不会产生 KeyError 问题 151 | ``` 152 | 153 | 写入: 154 | 155 | 156 | ``` Python 157 | from flask import make_response 158 | 159 | @app.route('/') 160 | def index(): 161 | response = make_response(render_template(...)) 162 | response.set_cookie('username', 'the username') 163 | return response 164 | ``` 165 | 166 | 实际上可以对 response 的 header 进行任何修改。 167 | 168 | ### 10、重定向和错误 169 | 170 | 使用 redirect() 函数可以重定向。使用 abort() 可以 更早退出请求,并返回错误代码: 171 | 172 | ``` Python 173 | from flask import abort, redirect, url_for 174 | 175 | @app.route('/') 176 | def index(): 177 | return redirect(url_for('login')) 178 | # 注意上面再次用到了 url_for 179 | 180 | @app.route('/login') 181 | def login(): 182 | abort(401) # 这里提前结束了请求,并返回401错误 183 | this_is_never_executed() 184 | ``` 185 | 186 | 通过 `errorhandler()` 装饰器定制出错页面: 187 | 188 | ``` Python 189 | from flask import render_template 190 | 191 | @app.errorhandler(404) 192 | def page_not_found(error): 193 | return render_template('page_not_found.html'), 404 194 | ``` 195 | 196 | 如此所有的 404 错误都会返回一个固定的页面,有点类似于切面。 197 | 198 | ### 11、关于响应 199 | 200 | 通过前面学习可以发现,无论返回内容是字符串,还是 `render_template()` 结果,亦或是 `make_response()`,都会被转换为一个 response,下面是转换的规则: 201 | 202 | 1. 如果视图返回的是一个响应对象,那么就直接返回它。 203 | 2. 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。 204 | 3. 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由 (response, status, headers) 或者 (response, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表或字典。 205 | 4. 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象 206 | 207 | ### 12、日志 208 | 209 | Flask 0.3 后就已经配置好一个日志工具。logger 是一个标准的 Logger 类,下面是一些调用的实例 210 | 211 | ``` Python 212 | app.logger.debug('A value for debugging') 213 | app.logger.warning('A warning occurred (%d apples)', 42) 214 | app.logger.error('An error occurred') 215 | ``` 216 | 217 | ### 13、WSGI 218 | 219 | WSGI即 Web Server Gateway Interface,是一个规范,它定义了Web服务器如何与Python应用进行交互,使得Python应用(就是我们借助 flask 写的这个东西)可以和 Web 服务器相互对接。简单概括: 220 | 221 | ``` 222 | -------- request ------------ parsed request ----------------- 223 | | |------------------->| | ----------------------> | | 224 | | Clinet | | Web server | | Web Application | 225 | | |<-------------------| | <---------------------- | | 226 | -------- packaged response ------------ response ----------------- 227 | 228 | ``` 229 | WSGI Middleware(中间件)也是WSGI规范的一部分,即处于 Web server 与 Web Application 之间的 230 | 231 | ### 参考 232 | 233 | - [Flask中文文档](https://dormousehole.readthedocs.io) 234 | - [WSGI简介](https://segmentfault.com/a/1190000003069785) 235 | - [李雪峰--Python教程](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432012393132788f71e0edad4676a3f76ac7776f3a16000) 236 | -------------------------------------------------------------------------------- /OSFoundation/Exceptional_Control_Flow.md: -------------------------------------------------------------------------------- 1 | # 【笔记】ECF Exceptional Control Flow 2 | 3 | 该文章是《深入理解计算机系统》中第八章关于 ECF 的读书笔记,有大量的原文摘抄,所以本篇文章不在仓库的许可协议之内。如您想了解相关知识请积极购买正版。 4 | 5 | ## ECF 6 | 7 | 实际上就是一种反馈机制,因为处理一系列事件,并不能始终是按照预定计划进行的,当意料之外的事件发生时,如何处理呢?如何将意外事件反馈给决策者呢?这就是需要意外处理,即本章要介绍的 ECF 8 | 9 | 当子进程终止时,创建这些子进程的父进程必须得到通知。(_为什么要通知父进程呢?他们不应该是相互独立的吗?_) 10 | 11 | 为什么要理解 ECF : 12 | 13 | - 有助于理解操作系统概念:ECF 是操作系统用来实现 I/O、进程和虚拟内存的基本机制; 14 | - 有助于理解应用程序与操作系统的交互:应用程序通过将使用一个叫做陷阱(trap)或者系统调用(system call)的 ECF形式,想操作系统请求服务。例如:向磁盘写数据、从网络读取数据、创建一个新进程,以及终止当前进程,都是通过应用程序调用系统调用来实现的; 15 | - 有助于编写应用:操作系统为应用程序提供了强大的 ECF 机制,用来创建新进程、等待进程终止、通知其它进程系统中的一场时间,以及检测和响应这些时间。 16 | - 有助于理解并发:ECF 是计算机系统中实现并发的基本机制。在运行中的并发的例子有:中断应用程序执行的异常处理程序,在时间上重叠执行的进程和线程,以及中断应用程序执行的信号处理程序。 17 | - 有助于理解软件异常如何工作:像 try、catch、throw 等异常机制,软件异常允许程序进行非本地跳转(即违反通常的调用/返回栈规则的跳转)来响应错误情况。非本地跳转是一种应用层 ECF,在 C 中是通过 setjmp 和 longjmp 函数提供的。 18 | 19 | ## 8.1 异常 20 | 21 | 异常是异常控制流的一种形式,它一部分由硬件实现,一部分由操作系统实现。 22 | 23 | 异常(exception)就是控制流中的突变,用来响应处理状态中的某些变化。 24 | 25 | 在任何情况下,当处理器检测到有时间发生时,它就会通过一个张叫做异常表(exception table)的跳转表,进行一个间接过程调用(异常),到一个专门设计用来处理这类事件的操作系统子程序(异常处理程序(exception handler))。异常处理完毕后,根据事件的类型,会发生一下 3 种情况之一: 26 | 1. 处理程序将控制返回给正常流的当前指令Icur,即当事件发生时正在执行的指令; 27 | 2. 处理程序将控制返回给正常流的下一条指令Inext,即如果没发生异常将会执行的下一条指令; 28 | 3. 处理程序终止被中断的程序 29 | 30 | ### 8.1.1 异常处理 31 | 32 | 系统为可能发生的每中类型异常都分配了一个唯一的非负整数的异常好(exception number)。其中一部分有处理器的设计者分配,其它则有操作系统内核(操作系统常驻内存的部分)的设计者分配 33 | 34 | CPU异常: 35 | 36 | - 被零除 37 | - 缺页 38 | - 内存访问违例 39 | - 断点 40 | - 算数运算溢出 41 | 42 | 系统异常: 43 | 44 | - 系统调用 45 | - 外部 I/O 设备的信号 46 | 47 | 在系统启动时,操作系统会分配和初始化一张成为异常表(exception table)的跳转表,表目 k (exception number)包含异常 k 的处理程序的地址。 48 | 49 | *异常表的起始地址放在一个叫做异常表基址计算器(exception table base register)的特殊 CPU 寄存器中*。 50 | 51 | 异常与过程调用不同的地方: 52 | 53 | - 过程调用,在跳转处理程序之前,处理器将返回地址压入栈中。而异常处理是根据异常的类型,返回cur或者next指令 54 | - 处理器会将一些额外的处理器状态压入栈中,因为在返回正常流时需要用到。 55 | - 如果控制从用户程序转移到内核,所有这些项目都被压到内核栈中,而不是用户栈。 56 | - 异常处理程序运行在内核模式下,这意味着他们对多有的系统资源都有完全的访问权限。 57 | 58 | 异常处理程序处理完异常事件之后,它通过执行一条特殊的“从中断返回”指令,可选地返回到被中断的程序,该指令将适当的状态弹回到处理器的控制和数据寄存器中,如果异常中断的是一个用户程序,就将状态恢复为用户模式,然后在将控制返回给被中断的程序。 59 | 60 | ### 8.1.2 异常的类别 61 | 62 | 异常分为四类: 63 | 64 | |类别|原因|同步/异步|返回行为| 65 | |:--|:---|:-------|:-----| 66 | |中断(interrupt)|来自 I/O 设备的信号|异步|总是返回到下一条指令| 67 | |陷阱(trap) |有意的异常|同步|总是返回到下一条指令| 68 | |故障(fault)|潜在可恢复的错误|同步|可能返回到当前指令| 69 | |终止(abort)|不可恢复的错误|同步|不会返回| 70 | 71 | #### 中断 72 | 73 | 当网络适配器、磁盘控制器、或定时器芯片,通过向处理器芯片上的一个引脚发信号,并将异常号放到系统总线上,来触发中断,该异常号标识了引起中断的设备。 74 | 75 | 当指令完成执行后,处理器注意到中断引脚的电压变高了,就从系统总线读取异常号,然后调用适当的中断处理程序。处理程序返回时,它就讲控制返回给下一条指令。 76 | 77 | *相当于暂停,所以返回时从下一条指令开始,而故障则相当于错误,所以返回时从当前指令开始,以重复该过程*。 78 | 79 | #### 陷阱和系统调用 80 | 81 | 陷阱是有意的异常,是执行一条指令的结果。其最重要的用途是在用户程序和内核之间提供一个像过程一样的借口,即系统调用。 82 | 83 | 用户程序经常需要向内核请求服务,诸如:读取文件(read)、创建新进程(fork)、加载一个新程序(execve),或者终止当前进程(exit)。处理器为了这些对内核的受控访问提供了一条特殊的“syscall n” 指令,用户程序执行该指令时就会导致一个到异常处理程序的陷阱,这个处理程序解析参数,并调用适当的内核程序。 84 | 85 | 普通函数运行在用户态,用户态限制了函数可以执行的指令类型,只能访问与调用函数相同的栈;系统调用运行在内核态,内核态允许系统调用执行一些特权指令,以及访问内核栈。 86 | 87 | #### 故障 88 | 89 | 故障是有错误情况引起的,如果故障处理程序能够修正该错误,那么会返回当前指令,再次执行。否则,处理程序返回到内核中的 abort 例程,终止引起故障的应用程序。 90 | 91 | #### 终止 92 | 93 | 终止是由不可恢复的致命错误造成的。终止处理程序不会将控制返回给应用。而是直接返回给内核中的 abort 例程,今儿终止应用程序。(终止与故障修改失败的处理方式相同) 94 | 95 | ### 8.1.3 Linux/x86-64系统中的异常 96 | 共有256中不同的异常类型: 97 | 98 | - 0~31号异常由Intel脚骨师定义 99 | - 32~255号异常由操作系统定义的中断和陷阱。 100 | 101 | ## 8.2 进程 102 | 103 | 进程:一个执行中程序的实例。 104 | 上下文(个人倾向译作--环境,语境,即能提供程序运行所需的环境,类似生物生物学中的生态环境):由程序正确运行所需的状态构成。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。 105 | 106 | 进程提供给应用程序2个关键抽象: 107 | 108 | - 一个独立的逻辑控制流,看上去好像应用程序在独占地使用 CPU 109 | - 一个私有的地址空间,看上去好像应用程序在独占地使用内存系统。 110 | 111 | ### 8.2.1 逻辑控制流 112 | 113 | 分时操作系统的概念,多个进程交替切换执行。 114 | 115 | ### 8.2.2 并发流 116 | 117 | 并发流(concurrent flow):一个逻辑流的执行在时间上与另一个流重叠,这两个流被称为并发地运行。即 X 在 Y 从开始到结束这段时间开始,或 X Y 的时机互换。(其实就是运行时间片有交替的多个流)。 118 | 119 | 多个流并发地执行的一般现象被称为并发(concurrency)。 120 | 121 | 一个进程和其它进程轮流运行的概念被称为多任务(multitasking)。一个进程执行它的控制流的一部分的每一个时间段叫做时间片(time slice)。因此,多任务也成做时间分片(time slicing)。 122 | 123 | **并发流的思想与流运行的处理器核数活着计算机数无关** 124 | 125 | 如果两个流并发地运行在不同的处理器核活着计算机上,那么我们称他们为并行流(parallel flow),他们并行地运行(running in parallel),且并行地执行(parallel excution)。 126 | 127 | ### 8.2.3 私有地址空间 128 | 129 | 在一台 n 位地址地机器上,地址空间是 2ⁿ 个可能地的集合。进程为每个程序提供它们自己的私有地址空间。 130 | 131 | 进程地址空间: 132 | 133 | ![Address space](./images/address_space.png) 134 | 135 | ### 8.2.4 用户模式和内核模式 136 | 137 | 处理器通过某个控制寄存器的一个模式位(mode bit)来提供这种限制功能,该寄存器描述了进程当前享有的特权。 138 | 139 | 当设置了模式位时,进程就运行在内核模式中。内核模式的进程可以执行指令集中的任何指令,且可以访问系统中的任意内存位置。 140 | 141 | 未设置模式位时,进程运行在用户模式。操作指令受限,访问范围受限。可以通过系统提供的接口间接地访问内核代码和数据。 142 | 143 | *进程初始都是用户模式,只有通过异常处理才能从用户态切换为内核态(正常情况下,否则也不会有被黑了)* 144 | 145 | ### 8.2.5 上下文切换 146 | 147 | 操作系统内核使用一种称为上下文切换(context switch)的较高层形式的异常控制流来实现多任务。该机制建立在8.1节中的异常机制之上。 148 | 149 | 内核为每个进程维持一个上下文。上下文包含内容(不全): 150 | 151 | - 通用目的寄存器 152 | - 浮点寄存器 153 | - 程序计数器 154 | - 用户栈 155 | - 状态寄存器 156 | - 内核栈 157 | - 描述地址空间的也表 158 | - 进程表 159 | - 进程已打开文件的文件表 160 | 161 | 抢占当前进程,重开先前被抢占了的进程的决策,被称为调度(scheduling),由内核中称为调度器(scheduler)的代码处理。 162 | 163 | 上下文切换: 164 | 165 | 1. 保存当前进程的上下文 166 | 2. 恢复先前被抢占进程的上下文 167 | 3. 将控制传递给恢复的进程 168 | 169 | ## 8.3 系统调用错误处理 170 | 171 | 很多 C 写的函数都喜欢用 -1 表示发生错误,不知道这个传统是不是就是从 Unix 开始的。 172 | 173 | 一个处理 `fork()` 错误的例子: 174 | 175 | ``` C 176 | if ((pid = fork()) < 0) { 177 | fprintf(stderr, "fork error: %s\n", strerror(errno)); 178 | exit(0) 179 | } 180 | ``` 181 | 182 | 书中建议对这类处理进行包装,这个没什么说的,常用的错误肯定要包装一个通用的错误处理函数。 183 | 184 | ## 8.4 进程控制 185 | 186 | 从这里开始就是一些实操了 187 | 188 | ### 8.4.1 获取进程 ID 189 | 190 | 每个进程都有一个唯一的正数(非零)进程 ID(PID)。`getpid(void)` 返回调用进程的 PID 191 | 192 | ### 8.4.2 创建和终止进程 193 | 194 | 收到一下信号时,进程会被挂起(suspended),直至收到 SIGCONT 再次恢复运行: 195 | 196 | - SIGSTOP 197 | - SIGTSTP 198 | - SIGTTIN 199 | - SIGTTOU 200 | 201 | 进程的三个终止原因: 202 | 203 | - 收到终止信号 204 | - 从主程序返回,即 `main()` 函数的 `return`,也就是执行完毕 205 | - 调用 exit 函数,主动调用退出函数 206 | 207 | **父进程通过调用 `fork()` 函数创建一个新的运行的子进程,新创建的子进程几乎但不完全与父进程相同。子进程会得到与父进程用户级虚拟地址空间相同的(但是独立的)一份拷贝,包括代码和数据段、堆、共享库以及用户栈。还获得与父进程任何打开文件描述符相同的副本,这意味着父进程调用 `fork()` 时,子进程可以读写父进程中打开的任何文件。最大区别就是 PID 不同**。 208 | 209 | `fork()` 调用一次,返回两次:一次在调用进程(父进程)中,它返回子进程的 PID;一次是在新创建的子进程中,它返回 0。 210 | 211 | 212 | **下面的实例代码创建了4个进程,1->2->4** 213 | 214 | ``` C 215 | int main() 216 | { 217 | fork(); 218 | fork(); 219 | printf("hello\n"); 220 | exit(0); 221 | } 222 | ``` 223 | 224 | ### 8.4.3 回收子进程 225 | 226 | 一个被终止的进程,并不会被内核从系统各种立即清除。相反,会被保持在一种已中能够值的状态,直至被它的父进程回收(reaped)。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程,此时,该进程才真正消失。终止但尚未被回收的进程称为僵死进程(zombie)。 227 | 228 | 如果父进程先终止,那么内核会安排 init 进程成为它的孤儿进程的养父。init 进程的 PID 为 1,是在系统启动时由内核创建的,它是所有进程的祖先。 229 | 230 | **回收细节暂时跳过,待后续补充。** 231 | 232 | ### 8.4.4 让进程休眠 233 | 234 | `unsigned int sleep(unsigned int secs)` 235 | 236 | ### 8.4.5 加载并运行程序 237 | 238 | `int execve(const char *filename, const char *argv[], const char *envp[])` 239 | 240 | 241 | ## 8.5 信号 242 | 243 | 通过信号,进程和内核可以中断其它进程。 244 | 245 | ### 8.5.1 246 | 247 | 发送信号,进程可以向目标进程或自己发信号,要通过内核。 248 | 249 | 接收信号,如果有一个类型的信号在排队等待,那么后续该中类型信号都会被丢弃。进程可以有选择性地阻塞接受某种信号。阻塞后,仍可被发送,但是产生的待处理信号不会被接收,直至取消阻塞。 250 | 251 | ### 8.5.2 发送信号 252 | 253 | #### 进程组 254 | 每个进程都只属于一个进程组,进程组是由一个正整数进程组 ID 来标识的。 255 | 256 | - `pid_t getpgrp(void)` 257 | - `int setpgid(pid_t pid, pid_t pgid)` 258 | 259 | ### 8.5.3 接收信号 260 | 261 | ### 8.5.4 阻塞和接触阻塞信号 262 | 263 | ### 8.5.5 信号处理程序 264 | 265 | ### 8.5.6 同步流以避免并发错误 266 | 267 | ### 8.5.7 显示地等待信号 268 | 269 | ## 8.6 非本地跳转 270 | 271 | 272 | 273 | 274 | 275 | -------------------------------------------------------------------------------- /OpenCV/OpenCV_images_and_large_array_types.md: -------------------------------------------------------------------------------- 1 | # 【笔记】OpenCv 之 Images 及大数组类型 2 | 3 | ## cv::Mat 4 | 用于描述任意维度的密集数组,例如图片。 5 | 6 | - flags 用来标识数组的内容 7 | - dims 表示维度 8 | - rows、cols 分别表示行数和列数(2 维以上无效) 9 | - data 数组指针 10 | - refcount 引用计数,这个稍稍复杂一些,主要是为了避免频繁的拷贝数据而开创的。 11 | 12 | ```cpp 13 | cv::Mat m; 14 | 15 | // 创建数据区,大小为 3 x 10,每个元素类型是 3 通道的 32 位 float 类型。 16 | m.create(3, 10, CV_32FC3); 17 | 18 | // 设置每个像素的三个通道,分别为 1.0、0.0、1.0 19 | m.setTo( cv::Scalar( 1.0f, 0.0f, 1.0f )); 20 | 21 | // 声明的同时初始化 22 | cv::Mat m(3, 10, CV_32FC3, cv::Scalar( 1.0f, 0.0f, 1.0f)); 23 | 24 | ``` 25 | 构造方法(无拷贝): 26 | 27 | ```cpp 28 | cv::Mat; 29 | cv::Mat(int rows, int cols, int type); 30 | cv::Mat(int rows, int cols, int type, const Scalar& s); 31 | cv::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP); 32 | cv::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP); 33 | cv::Mat(cv::Size sz, int type); 34 | cv::Mat(cv::Size sz, int type, const Scalar& s); 35 | cv::Mat(cv::Size sz, int type, void* data, size_t step=AUTO_STEP); 36 | cv::Mat(int ndims, const int* sizes, int type); 37 | cv::Mat(int ndims, const int* sizes, int type, const Scalar& s); 38 | cv::Mat(int ndims, const int* sizes, int type, void* data, size_t step=AUTO_STEP); 39 | ``` 40 | 41 | 构造方法(拷贝): 42 | 43 | ```cpp 44 | cv::Mat(const Mat& mat); 45 | cv::Mat(const Mat& mat, const cv::Range& rows, const cv::Range& cols); 46 | cv::Mat(const Mat& mat, const cv::Rect& roi); 47 | cv::Mat(const Mat& mat, const cv::Range* ranges); 48 | cv::Mat(cosnt cv::MatExpr& expr); 49 | ``` 50 | 51 | 构造方法(兼容2.1版): 52 | 53 | ```cpp 54 | cv::Mat(const CvMat* old, bool copyDate=false); 55 | cv::Mat(cosnt IplImage* old, bool copyData=false); 56 | ``` 57 | 58 | 模板构造器: 59 | 60 | ```cpp 61 | cv::Mat(const cv::Vec& vec, bool copyData=true); 62 | cv::Mat(const cv::Matx& vec, bool copyData=true); 63 | cv::Mat(const std::vector& vec, bool copyData=true); 64 | ``` 65 | 66 | 静态函数构造: 67 | 68 | ```cpp 69 | cv::Mat::zeros( rows, cols, type ); 70 | cv::Mat::ones( rows, cols, type ); 71 | cv::Mat::eye( rows, cols, type ); 72 | ``` 73 | 74 | 访问数组元素: 75 | 76 | ```cpp 77 | cv::Mat m = cv::Mat::eye(10, 10, 32FC1); 78 | printf("Element (3, 3) is %f\n", m.at(3, 3)); 79 | 80 | cv::Mat m = cv::Mat::eye(10, 10, 32FC2); 81 | printf("Element (3, 3) is (%f, %f)\n", 82 | m.at(3, 3)[0], 83 | m.at(3, 3)[1] 84 | ); 85 | 86 | cv::Mat m = cv::Mat::eye(10, 10, cv::DataType::type); 87 | printf( 88 | "Element (3, 3) is %f + i%f\n", 89 | m.at(3, 3).re, 90 | m.at(3, 3).im 91 | ); 92 | 93 | // 元素访问方法 94 | M.at(i); 95 | M.at(i, j); // Element (i, j) from float array M 96 | M.at(pt); // Element at location (pt.x, pt.y) in integer matrix M 97 | M.at(i, j, k);// Element at location (i, j, k) in three-demensional float array M 98 | M.at(idx); // Element at n-dimensional location indicated by idx[] in array M of unsigned characters 99 | ``` 100 | 101 | ### cv::NAryMatIterator 102 | 103 | ```cpp 104 | // 构建一个 3 维数组 105 | const int n_mat_size = 5; 106 | const int n_mat_sz[] = {n_mat_size, n_mat_size, n_mat_size }; 107 | cv::Mat n_mat(3, n_mat_sz, CV32FC1); 108 | 109 | // 向其中填充数据 110 | cv::RNG rng; 111 | rng.fill( n_mat, cv::RNG::UNIFORM, 0.f, 1.f ); 112 | 113 | 114 | // 首先,创建一个 C 数组,包含所有需要遍历的 mat 115 | // ⚠️注意,这个数组必须是以 0 或 `NULL` 结尾 116 | const cv::Mat* arrays[] = { &n_mat, 0 }; 117 | // 另一个 C 数组,用于指向各个平面 118 | // ⚠️注意,这里仅有一个,所以是 1 119 | cv::Mat my_planes[1]; 120 | // 所需参数准备就绪,创建迭代器 121 | cv::NAryMatIterator it( arrays, my_planes ); 122 | 123 | // N-ary iterator 创建完毕,开始对 m0 和 m1 求和 124 | // 125 | float s = 0.f; // 所有平面之和,为什么是 float ,注意回顾开始的声明 126 | int n = 0;// 平面数 127 | for (int p = 0; p < it.nplanes; p++, ++it) { 128 | s += cv::sum(it.planes[0])[0]; 129 | n++; 130 | } 131 | ``` 132 | 通过 N 元迭代器对两个数组进行求和 133 | 134 | ```cpp 135 | // 创建两个矩阵 136 | const int n_mat_size = 5; 137 | const int n_mat_sz[] = { n_mat_size, n_mat_size, n_mat_size }; 138 | cv::Mat n_mat0( 3, n_mat_sz, CV_32FC1 ); 139 | cv::Mat n_mat1( 3, n_mat_sz, CV_32FC1 ); 140 | 141 | // 填充数据 142 | cv::RNG rng; 143 | rng.fill( n_mat0, cv::RNG::UNIFORM, 0.f, 1.f ); 144 | rng.fill( n_mat1, cv::RNG::UNIFORM, 0.f, 1.f ); 145 | 146 | // 创建迭代器 147 | const cv::Mat* arrays[] = { &n_mat0, &n_mat1, 0 }; 148 | cv::Mat my_planes[2]; 149 | cv::NAryMatIterator it( arrays, my_planes ); 150 | 151 | // 准备求和 152 | float s = 0.f;// Total sum over all planes in both arrays 153 | int n = 0;// Total number of planes 154 | 155 | // 迭代求和 156 | for(int p = 0; p < it.nplanes; p++, ++it) { 157 | s += cv::sum(it.planes[0])[0]; 158 | s += cv::sum(it.planes[1])[0]; 159 | n++; 160 | } 161 | ``` 162 | 163 | ### 访问某一块元素 164 | 165 | ```cpp 166 | m.row(i); 167 | m.col(j); 168 | m.rowRange(i0, i1); 169 | m.rowRange(cv::Range(i0, i1)); 170 | m.colRange(j0, j1); 171 | m.colRange(cv::Range(j0, j1)); 172 | m.diag(d); 173 | m(cv::Range(i0, i1), cv::Range(j0, j1)); 174 | m(cv::Rect(i0, i1, w, h)); 175 | m(ranges); 176 | ``` 177 | ### 矩阵表达式 178 | 主要涉及的是矩阵运算 179 | 180 | ```cpp 181 | m0 + m1, m0 - m1; 182 | m0 + s; m0 -s; s + m0, s - m1;// s is singleton 183 | -m0; 184 | s * m0; m0 * s; 185 | m0.mul( m1 ); m0/m1; 186 | m0 * m1; 187 | m0.inv( method )// default value of method is DECOMP_LU 188 | m0.t()// Matrix transpose of m0 (no copy is done) 189 | m0 > m1; m0 >= m1; m0 == m1; m0 <= m1; m0 < m1; 190 | 191 | m0&m1; m0|m1; m0^m1; ~m0; 192 | m0&s; s&m0; m0|s; s|m0; m0^s; s^m0; 193 | 194 | min(m0,m1); max(m0,m1); min(m0,s); 195 | min(s,m0); max(m0,s); max(s,m0); 196 | 197 | cv::abs(m0); 198 | 199 | m0.cross(m1); m0.dot(m1); 200 | 201 | cv::Mat::eye( Nr, Nc, type ); 202 | cv::Mat::zeros( Nr, Nc, type ); 203 | cv::Mat::ones( Nr, Nc, type ); 204 | ``` 205 | 206 | ### 其它成员函数 207 | 208 | ```cpp 209 | m1 = m0.clone();// 会产生完整拷贝 210 | m0.copyTo(m1); // 与上面等价,如有必要会 reallocating m1 m0.copyTo(m1, mask); // 只有 mask 数组中标记的才会拷贝 211 | m0.convertTo(m1, type, scale, offset); 212 | m0.assignTo(m1, type);// 尽显内部调用 213 | // Set all entris in m0 to singleton value s; 214 | // if mask is present, set only those values corresponding to nonzero elements in mask 215 | m0.setTo(s, mask); 216 | // Changes effective of a two-dimensional matrix; chan or rows may be zero, which implies "no change"; ;data is not copied 217 | m0.reshape(chan, rows); 218 | m0.push_back(s); 219 | m0.push_back(m1); 220 | m0.pop_back(n); 221 | 222 | // Write whole size of m0 to cv::Size size; 223 | // if m0 is a 'view' of a large matrix, 224 | // write location of starting corner to Point& offset 225 | m0.locateROI(size, offset); 226 | // Increase the size of a view by t pixels above, b pixels below, l pixels to the left, and r pixels to the right 227 | m0.adjustROI(t, b, l, r); 228 | m0.total();// Compute the total number of array elements(does not include channels) 229 | m0.isContinuous();// Return true only if the rows in m0 are packed without space between them in memory 230 | m0.elemSize();// In bytes, e.g., a three-channel float matrix world return 12 bytes 231 | m0.elemSize1();// Subelements of m0 in bytes. e.g., a three-channel float matrix would return 4 bytes 232 | m0.type() 233 | m0.depth(); 234 | m0.channels(); 235 | m0.size();// Return the size of the m0 as a cv::Size object 236 | m0.empty(); 237 | ``` 238 | 239 | ## cv::SparseMat 240 | 241 | 线性代数计算?暂时跳过。 -------------------------------------------------------------------------------- /Ethereum/Browser-solidity_Installation_&_Introduction.md: -------------------------------------------------------------------------------- 1 | # Remix-ide(Browser-Solidity)的本地安装及使用介绍 2 | 3 | 正所谓工欲善其事必先利其器,巧妇也难为无米之炊,所以在学习智能合约之前,必须要先把工具准备好。Browser-Solidity 是 Ethereum 官方的一个IDE项目,该名称准确的表述了,它是一款基于浏览器的 Solidity 集成开发环境,官方正式名称称之为 Remix,通过它我们可以编辑、编译、发布我们自己的智能合约。 4 | 5 | 基于浏览器的开发环境,那么它有在线版吗?有,就是这里[https://remix.ethereum.org](https://remix.ethereum.org)。既然有在线版为什么还要安装呢?你亲身访问一下就能发现答案了。 6 | 7 | ## 环境准备 8 | 9 | 我们以 Mac 为开发平台,且 Mac上已经装好了 npm 和 Node.js,如果你的 Mac 上没有安装,那么你可以参考这里的教程:[How to Install npm & Manage npm Versions](https://docs.npmjs.com/getting-started/installing-node) 10 | 11 | 2018年2月8日补充:今天我手贱更新了一下Browser-solidity,重新执行`npm install`,然后 solc 这个包总是不成功,直到 `brew install wget`。 12 | 13 | 2018年4月10日更新:官方的Readme中已经明确指明,如果采用Clone到本地安装到方式,必须先装好 `wget`。 14 | 15 | ## 安装&启动 16 | 17 | ### 通过npm直接安装 18 | 19 | 以太坊真是日新月异,如新生儿般一天一个样。废话不多说,Remix已正式更名为Remix-ide,官方也已将其发布为npm包,可以通过npm直接安装,方便了很多: 20 | 21 | ``` Shell 22 | npm install remix-ide -g 23 | ``` 24 | 25 | 启动Remix-ide,安装成功后,直接在命令行键入如下命令启动。 26 | 27 | ``` Shell 28 | remix-ide 29 | ``` 30 | 31 | 然后打开 Chrome,或 Safari,在地址中输入:[localhost:8080](http:localhost:8080)即可。 32 | 33 | *下面的clone安装方法已不在推荐,可以选择性阅读。* 34 | 35 | ------------------------------------------------------------------------------------ 36 | 37 | 38 | 39 | ### clone项目到本地安装 40 | 41 | 打开终端,进入你准备的好安装目录,将 browser-solidity 项目 clone 到本地。 42 | 43 | ``` 44 | git clone https://github.com/ethereum/browser-solidity 45 | cd browser-solidity 46 | ``` 47 | 48 | 进入 browser-solidity 目录后,运行如下命令,开始安装。 49 | 50 | ``` 51 | npm install 52 | npm run 53 | ``` 54 | 安装完毕之后,输入如下命令,开启 Remix 本地服务。 55 | 56 | ``` 57 | npm start 58 | ``` 59 | 成功启动后,终端会输出如下内容,主要是一些与启动、参数说明和警告等相关信息: 60 | 61 | ``` 62 | > browser-solidity@0.0.0 start /Users/dracarys/Documents/Ethereum/browser-solidity 63 | > npm-run-all -lpr serve watch onchange remixd 64 | 65 | [remixd ] 66 | [remixd ] > browser-solidity@0.0.0 remixd /Users/dracarys/Documents/Ethereum/browser-solidity 67 | [remixd ] > node ./node_modules/remixd/src/main.js -s ./contracts 68 | [remixd ] 69 | [onchange] 70 | [onchange] > browser-solidity@0.0.0 onchange /Users/dracarys/Documents/Ethereum/browser-solidity 71 | [onchange] > onchange build/app.js -- npm-run-all lint 72 | [onchange] 73 | [watch ] 74 | [watch ] > browser-solidity@0.0.0 watch /Users/dracarys/Documents/Ethereum/browser-solidity 75 | [watch ] > watchify src/index.js -dv -p browserify-reload -o build/app.js 76 | [watch ] 77 | [serve ] 78 | [serve ] > browser-solidity@0.0.0 serve /Users/dracarys/Documents/Ethereum/browser-solidity 79 | [serve ] > execr --silent http-server . 80 | [serve ] 81 | [remixd ] example: --dev-path /home/devchains/chain1 --mist --geth --frontend /home/frontend --frontend-port 8084 --auto-mine 82 | [remixd ] 83 | [remixd ] Usage: main -S 84 | [remixd ] 85 | [remixd ] Provide a two ways connection between the local computer and Remix IDE 86 | [remixd ] 87 | [remixd ] 88 | [remixd ] Options: 89 | [remixd ] 90 | [remixd ] -s, --shared-folder Folder to share with Remix IDE 91 | [remixd ] -m, --mist start mist 92 | [remixd ] -g, --geth start geth 93 | [remixd ] -p, --dev-path Folder used by mist/geth to start the development instance 94 | [remixd ] -f, --frontend Folder that should be served by remixd 95 | [remixd ] -p, --frontend-port Http port used by the frontend (default 8082) 96 | [remixd ] -a, --auto-mine mine pending transactions 97 | [remixd ] -r, --rpc start rpc server. Values are CORS domain 98 | [remixd ] -rp, --rpc-port rpc server port (default 8545) 99 | [remixd ] -h, --help output usage information 100 | [remixd ] [WARN] Any application that runs on your computer can potentially read from and write to all files in the directory. 101 | [remixd ] [WARN] Symbolinc links are not forwarded to Remix IDE 102 | [remixd ] 103 | [remixd ] [WARN] Symbolic link modification not allowed : ./contracts | /Users/dracarys/Documents/Ethereum/browser-solidity/contracts 104 | [remixd ] Shared folder : ./contracts 105 | [remixd ] Mon Feb 05 2018 09:35:05 GMT+0800 (CST) Remixd is listening on 127.0.0.1:65520 106 | [watch ] WS server listening on 49815 107 | ``` 108 | 这时打开浏览器,在地址栏中输入:[http://127.0.0.1:8080](http://127.0.0.1:8080) 即可。 109 | 110 | ## 界面介绍 111 | 112 | Remix 界面入下图所示: 113 | 114 | ![Remix IDE](https://github.com/Dracarys/Articles/raw/master/images/full.png) 115 | 116 | 如果您熟悉编程,接触过很多IDE,那么 Remix 的界面你一定也不陌生,所以你可以跳过该部分,对 Remix 的理解不会受到任何影响。 117 | 118 | 下面是对上图中五个功能区域的简单介绍: 119 | 120 | - 红色 工具栏,这里可以进行新建文件、打开文件,发布到Github、向另一个实例拷贝和建立本地连接等操作; 121 | - 橙色 文件导航区,在这里可以分常方便地在各个文件间进行切换; 122 | - 黄色 代码编辑区,可以点击上方的+-对代码文字进行放大和缩小,还可以单击上方的《、》符号隐藏左右边栏; 123 | - 绿色 信息输出区,这里可以看到代码的输出结果和编译信息,还可以通过该区域上方的下拉列表对信息进行过滤,和搜索; 124 | - 紫色 属性编辑区,该区域有六个分页,分别是:Compile、Run、Setting、Analysis、Debug、Support。下面我们会着重介绍区域的设置。 125 | 126 | ## 功能介绍 127 | 128 | 前四个区域没什么好介绍的,大家只要上手简单操作一下就能明白。这里我们着重介绍下区域5,属性编辑区。这里我么只介绍Setting、Run、Compile、Debug 四个个分页,剩余两个分页功能非常直观,如果不是理解建议大家自行查询,不再赘述。 129 | 130 | ### Setting IDE设置 131 | 132 | Setting界面如下图所示: 133 | 134 | ![Setting](https://github.com/Dracarys/Articles/raw/master/images/setting.png) 135 | 136 | `Solidity version` 设置 Solidity 的版本,这个的版本必须高于或者与你在源码中指定的版本相同,否则可能会导致一些无法预料的行为,非常关键。这里建议大家选择名字较短的稳定版,以减少不必要的麻烦。 137 | 138 | `General setting` 通用设置,这里可以设置以及文本断行,是否默认启用JavaScript VM,以及是否启用优化; 139 | 140 | `Theme` 主题设置,没什么好介绍的,只有2个主题,选择一个自己喜欢的即可。 141 | 142 | `Plugin` 插件,目前还处于Alpha阶段,如果不了解,请不要更改。(我就是那个不了解的,还望各位大牛指点) 143 | 144 | ### Run 运行设置 145 | 146 | Run界面如下图所示: 147 | 148 | ![Run](https://github.com/Dracarys/Articles/raw/master/images/run.png) 149 | 150 | #### 红色区域 151 | 152 | - `Environment` 运行环境选择,默认为本地 JavaScript VM,Web3 Provider 用于连接指定的虚拟机服务,可以用来本地测试或者小范围的局域网测试。还有一个 Injected Web3 功能暂时不明,望高人告知。 153 | - `Account` 账户,默认的 JavaScript VM 提供了 5 个虚拟账户,每个账户有100 ether,如果连接到 Web3 Provider,那么这里会显示你在这台 Provider 上的账户。 154 | - `Gas limit` Gas上限,并不是越大越好,当然也不能太小,否则会影响交易。有关Gas的进一步信息,你可以访问这里[Gas and ether](http://www.ethdocs.org/en/latest/ether.html#gas-and-ether) 155 | - `Value` 即Gas price,输入后不要忘记在右侧的下拉列表中设置价格单位。如果你不熟悉 Wei 和 Ether 之间的换算可以参见下表 156 | 157 | |单位|换算率|Wei| 158 | |:--|:--|:--| 159 | |wei|1|1| 160 | |Kwei (babbage)|1e3 wei |1,000| 161 | |Mwei (lovelace)| 1e6 wei |1,000,000| 162 | |Gwei (shannon)| 1e9 wei |1,000,000,000| 163 | |microether (szabo)|1e12 wei |1,000,000,000,000| 164 | |milliether (finney)|1e15 wei |1,000,000,000,000,000| 165 | |ether|1e18 wei|1,000,000,000,000,000,000| 166 | 167 | #### 橙色区域 168 | 169 | - `选择合约` 如果你有多个合约,那么可以在这里选择运行那个合约 170 | - `Create` 有些合约有初始值,那么可以在这里设置,然后在点击 Create 按钮,成功后才会出现绿色区域。 171 | - `At Address` 从指定的地址加载一个已经存在的合约,加载成功后会出现在绿色区域内 172 | 173 | #### 黄色区域 174 | 175 | 暂时不明,待补充。 176 | 177 | #### 绿色区域 178 | 只有成功运行才会显示该区域,在这里你可以对自己编写的合约各个方法进测试。例如上图,这里我只写了一个 pay 函数,那么就可以在这里输入数值,然后点击 pay 来查看输出结果,以便验证该函数是否运行正常。 179 | 180 | ### Compile 编译设置 181 | 182 | Compile 界面如下图所示: 183 | 184 | ![Compile](https://github.com/Dracarys/Articles/raw/master/images/compile.png) 185 | 186 | - `Start to compile` 点击该按钮即开始编译,这里我们勾选 auto compile,这样就不用我们每次都点了,而且还能及时帮我们发现语法上的错误。 187 | 188 | - `选择合约` 可以在多个合约间进行切换,注意,这里只有编译才能显示,如果未编译,这里将为空。 189 | - `Detail` 点击可以查看上一步已选择合约的详细信息(如下图) 190 | 191 | ![Detail](https://github.com/Dracarys/Articles/raw/master/images/detail.png) 192 | 193 | - `Publish on Swarm` 发布到 Swarm?*还不是很理解,猜测:跟选择的运行环境有关,发不到官方的测试链或者正式链?*望大牛提点。 194 | 195 | ### Debug 196 | 197 | Debug 界面如下图所示: 198 | 199 | ![Debug](https://github.com/Dracarys/Articles/raw/master/images/debug.png) 200 | 201 | ## 最后 202 | 203 | IDE的某些功能会随着使用逐渐加深了解,所以请不要在IDE的熟悉上花费太多时间,能上手操作即可,就像小时候我们学说话一样,相信你我都绝不是在能说绕口令之后才叫爸爸妈妈的。 204 | 205 | 未完待续! 206 | 207 | ## 参考 208 | 1. [browser-solidity Github 项目](https://github.com/ethereum/browser-solidity) 209 | 2. [What is ether?](http://www.ethdocs.org/en/latest/ether.html) 210 | 3. [编译和部署合约的第一种姿势:使用 Remix](http://ethfans.org/posts/deploying-smart-contract-with-remix) 211 | 4. [一步一步使用remix开发智能合约](http://www.cnblogs.com/baizx/p/7280224.html) -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_design_patterns.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之设计模式 2 | ## 1. 什么是设计模式? 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 | ### 1.1 工厂方法模式以及抽象工厂模式? 31 | 32 | - 工厂方法模式定义创建对象的接口,让子类决定实例化哪一个类。工厂方法是的一个类的实例化延迟到其子类。 33 | - 抽象工厂模式提供一个固定的接口,用于创建一系列有关联和相依存的对象,而不必制定其具体类或其创建的细节。客户端与从工厂得到的具体对象之间没有耦合。 34 | 35 | 对比: 36 | 37 | |工厂方法模式|抽象工厂模式| 38 | |:---------|:---------| 39 | |通过类继承创建抽象产品|通过对象组合创建抽象产品| 40 | |创建一种产品|创建多系列产品| 41 | |子类话创建者并冲在工厂方法以及创建新产品|必须修改父类的接口才能支持新的产品| 42 | 43 | ### 1.2 抽象工厂模式在 CocoaTouch 中的哪些类中有体现? 44 | 类簇是基础框架中一种常见的设计模式,基于抽象工厂模式的思想。它将若干相关的私有具体工厂子类集合到一个公有的抽象超类之下。 45 | 46 | CocoaTouch 中的类簇: 47 | 48 | - `NSNumber` 49 | - `UIButton` 50 | 51 | ### 1.3 单例模式 52 | 53 | ```objc 54 | + (instancetype)sharedInstance { 55 | static InstanceType *instance; 56 | static dispatch_once_t onceToken; 57 | dispatch_once(&onceToken, ^{ 58 | instance = [[InstanceType alloc] init]; 59 | }); 60 | return instance; 61 | } 62 | ``` 63 | 64 | Swift 已经不需如此处理,它已经从语义上保证只被初始化一次。 65 | 66 | ## 2. 面向对象 67 | 面向对象编程(Object Oriented Programming, OOP),是一种计算机编程架构,也是目前应用最广泛的编程设计原则。OOP 的一个基本原则是计算机程序是由单个能够收到子程序作用的单元或对象组合而成。 68 | 69 | 面向对象三大特性: 70 | 71 | - 封装性 72 | - 继承性 73 | - 多态性 74 | 75 | ### 2.1 面向对象设计七大原则 76 | 1. 单一职能原则(Single Responsibility Princple) 77 | 2. 里氏替换原则(Liskov Subsitution Princple) 78 | 3. 依赖倒置原则(Dependeance Inversion Princple) 79 | 4. 接口隔离原则(Interface Segregation Princple) 80 | 5. 开闭原则(Open Close Princple) 81 | 6. 迪米特发法则(Law of Demeter) 82 | 83 | > 摘录自 [卡奴达摩专栏](https://blog.csdn.net/zhengzhb/article/category/926691) 84 | 85 | #### 2.1.1 单一职责原则 86 | **定义:**不要存在多于一个导致类变更的原因。**通俗的说,即一个类只负责一项职责。 87 | 88 | **问题:**类 T 负责两个不同的职责:职责 P1,职责 P2。当由于职责 P1 需求发生改变而需要修改类 T 时,有可能会导致原本运行正常的职责 P2 功能发生故障。 89 | 90 | **解决:**遵循单一职责原则。分别建立两个类 T1、T2,使 T1 完成职责 P1 功能,T2 完成职责 P2 功能。这样,当修改 T1 时,不会使职责 P2 发生故障风险;同理,当修改 T2 时,也不会使职责 P1 发生故障风险。 91 | 92 | #### 2.1.2 里氏替换原则 93 | 1988年,由 MIT 的一位里性(Liskov)女士提出。如果对每一个类型为 T1 的对象 o1,都有类型 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都换成 o2 时,程序 P 的行为没有变化,那么类型 T2 是类型 T1 的子类型。 94 | 95 | 通俗地讲,即所有引用基类的地方必须能透明地使用其子类的对象。要复合这一原则,子类对基类就要只扩展,而不重写覆盖基类的实现。 96 | 97 | #### 2.1.3 依赖倒置原则 98 | **定义:**高层模块不应该依赖底层模块,二者都不应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 99 | 100 | **问题:**假设类 A 直接依赖类 B,如果要让 A 改为依赖类 C,那么必须修改 A 的代码。此时,如果 A 是一个高层模块,负责复杂的业务逻辑,而 B 和 C 是底层模块,负责基本的原子操作。那么修改后就会带来风险,因为 A 的业务逻辑复杂,依赖可能非常分散,而且也非常不便于测试。 101 | 102 | **解决:**通过协议(接口)来解决。假设我们将 A 的依赖全部抽象到一个协议 P 中,那么 B、C 只要实现协议即可,以后再次发生以来更新时,只要新增一个 D 并实现 AP 协议即可,这样就大大降低了修改类 A 的几率。 103 | 104 | #### 2.1.4 接口隔离原则 105 | **定义:**客户端不应该依赖它不需要的接口;一个类对另一类的依赖应该建立在最小的接口上。 106 | 107 | **问题:**类 A 通过接口 I 依赖类 B,类 C 通过接口 I 依赖类 D,如果接口 I 对于类 A 和类 B 来说不是最小接口,则类 B 和类 D 必须去实现他们不需要的方法。 108 | 109 | **解决:**将臃肿的接口 I 拆分为独立的几个接口,类 A 和类 C 分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则 110 | 111 | #### 2.1.5 开闭原则 112 | **定义:**一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 113 | 114 | **问题:**在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。 115 | 116 | **解决:**当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。 117 | 118 | 这一条最虚,跟没说一样,具体怎么做才能做到尽量不修改,只做加法即可?现实开发中不存在不修改原有的可能,只能尽量将粒度划分的小一些,以贴合这一设计原则。 119 | 120 | #### 2.1.6 迪米特法则 121 | **定义:**一个对象应该对其他对象保持最少的了解。 122 | 123 | **问题:**类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 124 | 125 | **解决:**尽量降低类与类之间的耦合 126 | 127 | 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。 128 | 129 | 迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。 130 | 131 | ## 3. 架构 132 | 架构(Architecture) 133 | 134 | - MVC: Model-View-Controller 135 | - MVP: Model-View-Presenter 136 | - MVVM:Model-View-View Model 137 | - VIPER: 138 | 139 | > 引自:[《iOS Architecture Patterns:Demystifying MVC, MVP, MVVM and VIPER》](https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52) 140 | > 141 | > 译文:[《iOS架构设计:揭秘MVC, MVP, MVVM以及VIPER》](https://www.jianshu.com/p/68aa6071c02e) 142 | 143 | ### 3.1 MVC 架构 144 | 145 | #### 3.1.1 传统的 MVC 架构 146 | ![传统的MVC架构](../images/architecture_traditional_mvc.png) 147 | 148 | 在上图的情况下,View是无状态的。一旦Model被改变,Controller就会简单地渲染它。例如:网页完全加载后,一旦你按下链接,就导航到其他地方。 149 | 虽然在iOS应用用传统的MVC架构也可以实现,但这并没有多大意义,由于架构问题 ——三个实体是紧耦合的,每个实体和其他两个通信。这大大降低了可重用性——这可不是你希望在你的应用程序看到的 150 | 151 | #### 3.1.2 Apple的MVC架构 152 | **期望** 153 | 154 | ![Apple期望的MVC架构](../images/architecture_apple_mvc.png) 155 | 156 | Controller是View和Model之间的中介,这样他们就解耦了。最小的可重用单元是Controller,这对我们来说是个好消息,因为我们必须有一个来放那些不适合放入Model的复杂业务逻辑的地方。 157 | 158 | 从理论上讲,它看起来很简单,但你觉得有些地方不对,对吧?你甚至听到有人说MVC全称应该改为Massive View Controller(大量的视图控制器)。此外,为View controller减负也成为iOS开发者面临的一个重要话题。 159 | 160 | **实际** 161 | 162 | ![Apple实际的MVC架构](../images/architecture_actual_mvc.png) 163 | 164 | Cocoa MVC鼓励人们编写大规模的视图控制器,而且由于它们涉及View的生命周期,所以很难说它们(View和Controller)是分离的。 165 | 166 | 虽然你仍有能力将一些业务逻辑和数据转换成Model,但你没办法将View从Controller中分离。在大多数时候所有View的责任是把事件传递给Controller 167 | 168 | #### 3.1.3 MVC 架构的优缺点 169 | MVC 的优点: 170 | 171 | - 各司其职,互不干涉:三层中某一个发生了需求变化,只需修改响应模块的逻辑可,不会影响到其它; 172 | - 有利于开发分工:正式因为其具备互不干涉的特点,得以将开发工作分开; 173 | - 有利于组件的重用:功能专一可以抽象成一些通用的组件,利于重用。 174 | 175 | MVC 的缺点: 176 | 177 | - 增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。 178 | - 视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。 179 | - 视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。 180 | - 目前,一般高级的界面工具或构造器不支持MVC模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。 181 | 182 | ### 3.2 MVP 架构 183 | MVP(Model View Presenter)架构师从著名的 MVC 架构演变而来的。虽然 MVC 很好的将 Mode 和 View 进行了分离,但是也带来一个问题,Model 的变化以及 View 的反馈,这些消息都必须经过 Controller 来进行,随着应用的开发,Controller 也就变得越来越臃肿不堪。 184 | 185 | ![MVP架构](../images/architecture_mvp.png) 186 | 187 | #### 3.2.1 MVP 架构介绍 188 | 从名字便可知,MVP 同样是三层架构: 189 | 190 | - Model:数据层,它区别于 MVC 架构中的 Model,在这里不仅仅只是数据模型,还兼具对数据的存取操作,例如:对数据库的读写,网络的数据的请求等等; 191 | - View:视图层,仅负责对数据的展示,提供与用户进行交互的窗口; 192 | - Presenter:它是连接 View 与 Model 的桥梁,并对业务逻辑进行处理。在 MVP 架构中 Model 与 View 无法直接进行交互。所以在 Presenter 中,它会从 Model 层中获得所需的数据,进行适当的处理后交给 View 进行展示。这样通过 Presenter 将 View 与 Model 进行隔离,使得 View 和 Model 之间不存在耦合,同时也将业务逻辑从 View 中抽离。 193 | 194 | #### 3.2.2 MVP 的特性评估 195 | - 职责拆分 — 我们将最主要的任务划分到Presenter和Model,而View的功能较少(虽然上述例子中Model的任务也并不多)。 196 | - 可测性 — 非常好,基于一个功能简单的View层,可以测试大多数业务逻辑 197 | - 易用性 — 在我们上边不切实际的简单的例子中,代码量是MVC模式的2倍,但同时MVP的概念却非常清晰 198 | 199 | #### 3.2.3 监听 Controller 的 MVP 200 | ![监听Controller的MVP](../images/architecture_observing_mvp.png) 201 | 202 | ### 3.3 MVVM 架构 203 | 204 | #### 3.3.1 MVVM 介绍 205 | MVVM架构是MV(X)系列最新的成员 206 | 207 | ![MVVM](../images/architecture_mvvm.png 208 | ) 209 | 210 | 它和 MVP 模式看起来很像: 211 | 212 | - MVVM 也将 ViewController 视作 View 213 | - 在 View 和Model 之间没有耦合 214 | 215 | 此外,它还有像 Supervising 版本的MVP那样的绑定功能,但这个绑定不是在 View 和 Model 之间而是在 View 和 ViewModel 之间 216 | 217 | #### 3.3.2 绑定 218 | 219 | #### 3.3.3 MVVM 的特性评估 220 | - 职责拆分 — 在例子中并不是很清晰,但是事实上,MVVM的View要比MVP中的View承担的责任多。因为前者通过ViewModel的设置绑定来更新状态,而后者只监听Presenter的事件但并不会对自己有什么更新。 221 | - 可测性 — ViewModel不知道关于View的任何事情,这允许我们可以轻易的测试ViewModel。同时View也可以被测试,但是由于属于UIKit的范畴,对他们的测试通常会被忽略。 222 | - 易用性 — 在我们例子中的代码量和MVP的差不多,但是在实际开发中,我们必须把View中的事件指向Presenter并且手动的来更新View,如果使用绑定的话,MVVM代码量将会小的多。 223 | 224 | ### 3.4 VIPER 架构 225 | 226 | #### 3.4.1 VIPER 架构介绍 227 | 228 | ![VIPER架构](../images/architecture_viper.png) 229 | 230 | - 交互器(Interactor) — 包括关于数据和网络请求的业务逻辑,例如创建一个实体(Entities),或者从服务器中获取一些数据。为了实现这些功能,需要使用服务、管理器,但是他们并不被认为是VIPER架构内的模块,而是外部依赖。 231 | - 展示器(Presenter) — 包含UI层面(但UIKit独立)的业务逻辑以及在交互器(Interactor)层面的方法调用。 232 | - 实体(Entities) — 普通的数据对象,不属于数据访问层,因为数据访问属于交互器(Interactor)的职责。 233 | - 路由器(Router) — 用来连接VIPER的各个模块。 234 | 235 | #### 3.4.2 VIPER 与 MVX 对比 236 | - Model(数据交互)逻辑以实体(Entities)为单位拆分到交互器(Interactor)中。 237 | - Controller/Presenter/ViewModel 的UI展示方面的职责移到了Presenter中,但是并没有数据转换相关的操作。 238 | - VIPER 是第一个通过路由器(Router)实现明确的地址导航的模式。 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Java/All_about_Java.md: -------------------------------------------------------------------------------- 1 | # Java 知识点 2 | 3 | ## 一、基础篇 4 | 5 | JVM 6 | 7 | JVM 内存结构 8 | 9 | 堆、栈、方法区、直接内存、堆和栈区别 10 | 11 | Java 内存模型 12 | 13 | 内存可见性、重排序、顺序一致性、volatile、锁、final 14 | 15 | 垃圾回收 16 | 17 | 内存分配策略、垃圾收集器( G1 )、GC 算法、GC 参数、对象存活的判定 18 | 19 | JVM 参数及调优 20 | 21 | Java 对象模型 22 | 23 | oop-klass、对象头 24 | 25 | HotSpot 26 | 27 | 即时编译器、编译优化 28 | 29 | 类加载机制 30 | 31 | classLoader、类加载过程、双亲委派(破坏双亲委派)、模块化( jboss modules、osgi、jigsaw ) 32 | 33 | 虚拟机性能监控与故障处理工具 34 | 35 | jps, jstack, jmap、jstat, jconsole, jinfo, jhat, javap, btrace、TProfiler 36 | 37 | 编译与反编译 38 | 39 | javac、javap、jad、CRF 40 | 41 | Java 基础知识 42 | 43 | 阅读源代码 44 | 45 | String、Integer、Long、Enum、BigDecimal、ThreadLocal、ClassLoader & URLClassLoader、ArrayList & LinkedList、HashMap & LinkedHashMap & TreeMap & CouncurrentHashMap、HashSet & LinkedHashSet & TreeSet 46 | 47 | Java 中各种变量类型 48 | 49 | 熟悉 Java String 的使用,熟悉 String 的各种函数 50 | 51 | JDK 6 和 JDK 7 中 substring 的原理及区别、 52 | 53 | replaceFirst、replaceAll、replace 区别、 54 | 55 | String 对“+”的重载、 56 | 57 | String.valueOf 和 Integer.toString 的区别、 58 | 59 | 字符串的不可变性 60 | 61 | 自动拆装箱 62 | 63 | Integer 的缓存机制 64 | 65 | 熟悉 Java 中各种关键字 66 | 67 | transient、instanceof、volatile、synchronized、final、static、const 原理及用法。 68 | 69 | 集合类 70 | 71 | 常用集合类的使用、ArrayList 和 LinkedList 和 Vector 的区别 、SynchronizedList 和 Vector 的区别、HashMap、HashTable、ConcurrentHashMap 区别、Java 8 中 stream 相关用法、apache 集合处理工具类的使用、不同版本的 JDK 中 HashMap 的实现的区别以及原因 72 | 73 | 枚举 74 | 75 | 枚举的用法、枚举与单例、Enum 类 76 | 77 | Java IO&Java NIO,并学会使用 78 | 79 | bio、nio 和 aio 的区别、三种 IO 的用法与原理、netty 80 | 81 | Java 反射与 javassist 82 | 83 | 反射与工厂模式、 java.lang.reflect.* 84 | 85 | Java 序列化 86 | 87 | 什么是序列化与反序列化、为什么序列化、序列化底层原理、序列化与单例模式、protobuf、为什么说序列化并不安全 88 | 89 | 注解 90 | 91 | 元注解、自定义注解、Java 中常用注解使用、注解与反射的结合 92 | 93 | JMS 94 | 95 | 什么是 Java 消息服务、JMS 消息传送模型 96 | 97 | JMX 98 | 99 | java.lang.management.*、 javax.management.* 100 | 101 | 泛型 102 | 103 | 泛型与继承、类型擦除、泛型中 K T V E ? object 等的含义、泛型各种用法 104 | 105 | 单元测试 106 | 107 | junit、mock、mockito、内存数据库( h2 ) 108 | 109 | 正则表达式 110 | 111 | java.lang.util.regex.* 112 | 113 | 常用的 Java 工具库 114 | 115 | commons.lang, commons.*... guava-libraries netty 116 | 117 | 什么是 API&SPI 118 | 119 | 异常 120 | 121 | 异常类型、正确处理异常、自定义异常 122 | 123 | 时间处理 124 | 125 | 时区、时令、Java 中时间 API 126 | 127 | 编码方式 128 | 129 | 解决乱码问题、常用编码方式 130 | 131 | 语法糖 132 | 133 | Java 中语法糖原理、解语法糖 134 | 135 | Java 并发编程 136 | 137 | 什么是线程,与进程的区别 138 | 139 | 阅读源代码,并学会使用 140 | 141 | Thread、Runnable、Callable、ReentrantLock、ReentrantReadWriteLock、Atomic*、Semaphore、CountDownLatch、、ConcurrentHashMap、Executors 142 | 143 | 线程池 144 | 145 | 自己设计线程池、submit() 和 execute() 146 | 147 | 线程安全 148 | 149 | 死锁、死锁如何排查、Java 线程调度、线程安全和内存模型的关系 150 | 151 | 锁 152 | 153 | CAS、乐观锁与悲观锁、数据库相关锁机制、分布式锁、偏向锁、轻量级锁、重量级锁、monitor、锁优化、锁消除、锁粗化、自旋锁、可重入锁、阻塞锁、死锁 154 | 155 | 死锁 156 | 157 | volatile 158 | 159 | happens-before、编译器指令重排和 CPU 指令重 160 | 161 | synchronized 162 | 163 | synchronized 是如何实现的? synchronized 和 lock 之间关系、不使用 synchronized 如何实现一个线程安全的单例 164 | 165 | sleep 和 wait 166 | 167 | wait 和 notify 168 | 169 | notify 和 notifyAll 170 | 171 | ThreadLocal 172 | 173 | 写一个死锁的程序 174 | 175 | 写代码来解决生产者消费者问题 176 | 177 | 守护线程 178 | 179 | 守护线程和非守护线程的区别以及用法 180 | 181 | ## 二、 进阶篇 182 | 183 | Java 底层知识 184 | 185 | 字节码、class 文件格式 186 | 187 | CPU 缓存,L1,L2,L3 和伪共享 188 | 189 | 尾递归 190 | 191 | 位运算 192 | 193 | 用位运算实现加、减、乘、除、取余 194 | 195 | 设计模式 196 | 197 | 了解 23 种设计模式 198 | 199 | 会使用常用设计模式 200 | 201 | 单例、策略、工厂、适配器、责任链。 202 | 203 | 实现 AOP 204 | 205 | 实现 IOC 206 | 207 | 不用 synchronized 和 lock,实现线程安全的单例模式 208 | 209 | nio 和 reactor 设计模式 210 | 211 | 网络编程知识 212 | 213 | tcp、udp、http、https 等常用协议 214 | 215 | 三次握手与四次关闭、流量控制和拥塞控制、OSI 七层模型、tcp 粘包与拆包 216 | 217 | http/1.0 http/1.1 http/2 之前的区别 218 | 219 | Java RMI,Socket,HttpClient 220 | 221 | cookie 与 session 222 | 223 | cookie 被禁用,如何实现 session 224 | 225 | 用 Java 写一个简单的静态文件的 HTTP 服务器 226 | 227 | 实现客户端缓存功能,支持返回 304 实现可并发下载一个文件 使用线程池处理客户端请求 使用 nio 处理客户端请求 支持简单的 rewrite 规则 上述功能在实现的时候需要满足“开闭原则” 228 | 229 | 了解 nginx 和 apache 服务器的特性并搭建一个对应的服务器 230 | 231 | 用 Java 实现 FTP、SMTP 协议 232 | 233 | 进程间通讯的方式 234 | 235 | 什么是 CDN ?如果实现? 236 | 237 | 什么是 DNS ? 238 | 239 | 反向代理 240 | 241 | 框架知识 242 | 243 | Servlet 线程安全问题 244 | 245 | Servlet 中的 filter 和 listener 246 | 247 | Hibernate 的缓存机制 248 | 249 | Hiberate 的懒加载 250 | 251 | Spring Bean 的初始化 252 | 253 | Spring 的 AOP 原理 254 | 255 | 自己实现 Spring 的 IOC 256 | 257 | Spring MVC 258 | 259 | Spring Boot2.0 260 | 261 | Spring Boot 的 starter 原理,自己实现一个 starter 262 | 263 | Spring Security 264 | 265 | 应用服务器知识 266 | 267 | JBoss 268 | 269 | tomcat 270 | 271 | jetty 272 | 273 | Weblogic 274 | 275 | 工具 276 | 277 | git & svn 278 | 279 | maven & gradle 280 | 281 | ## 三、 高级篇 282 | 283 | 新技术 284 | 285 | Java 8 286 | 287 | lambda 表达式、Stream API、 288 | 289 | Java 9 290 | 291 | Jigsaw、Jshell、Reactive Streams 292 | 293 | Java 10 294 | 295 | 局部变量类型推断、G1 的并行 Full GC、ThreadLocal 握手机制 296 | 297 | Spring 5 298 | 299 | 响应式编程 300 | 301 | Spring Boot 2.0 302 | 303 | 性能优化 304 | 305 | 使用单例、使用 Future 模式、使用线程池、选择就绪、减少上下文切换、减少锁粒度、数据压缩、结果缓存 306 | 307 | 线上问题分析 308 | 309 | dump 获取 310 | 311 | 线程 Dump、内存 Dump、gc 情况 312 | 313 | dump 分析 314 | 315 | 分析死锁、分析内存泄露 316 | 317 | 自己编写各种 outofmemory,stackoverflow 程序 318 | 319 | HeapOutOfMemory、Young OutOfMemory、MethodArea OutOfMemory、ConstantPool OutOfMemory、DirectMemory OutOfMemory、Stack OutOfMemory Stack OverFlow 320 | 321 | 常见问题解决思路 322 | 323 | 内存溢出、线程死锁、类加载冲突 324 | 325 | 使用工具尝试解决以下问题,并写下总结 326 | 327 | 当一个 Java 程序响应很慢时如何查找问题、 328 | 329 | 当一个 Java 程序频繁 FullGC 时如何解决问题、 330 | 331 | 如何查看垃圾回收日志、 332 | 333 | 当一个 Java 应用发生 OutOfMemory 时该如何解决、 334 | 335 | 如何判断是否出现死锁、 336 | 337 | 如何判断是否存在内存泄露 338 | 339 | 编译原理知识 340 | 341 | 编译与反编译 342 | 343 | Java 代码的编译与反编译 344 | 345 | Java 的反编译工具 346 | 347 | 词法分析,语法分析( LL 算法,递归下降算法,LR 算法),语义分析,运行时环境,中间代码,代码生成,代码优化 348 | 349 | 操作系统知识 350 | 351 | Linux 的常用命令 352 | 353 | 进程同步 354 | 355 | 缓冲区溢出 356 | 357 | 分段和分页 358 | 359 | 虚拟内存与主存 360 | 361 | 数据库知识 362 | 363 | MySql 执行引擎 364 | 365 | MySQL 执行计划 366 | 367 | 如何查看执行计划,如何根据执行计划进行 SQL 优化 368 | 369 | SQL 优化 370 | 371 | 事务 372 | 373 | 事务的隔离级别、事务能不能实现锁的功能 374 | 375 | 数据库锁 376 | 377 | 行锁、表锁、使用数据库锁实现乐观锁、 378 | 379 | 数据库主备搭建 380 | 381 | binlog 382 | 383 | 内存数据库 384 | 385 | h2 386 | 387 | 常用的 nosql 数据库 388 | 389 | redis、memcached 390 | 391 | 分别使用数据库锁、NoSql 实现分布式锁 392 | 393 | 性能调优 394 | 395 | 数据结构与算法知识 396 | 397 | 简单的数据结构 398 | 399 | 栈、队列、链表、数组、哈希表、 400 | 401 | 树 402 | 403 | 二叉树、字典树、平衡树、排序树、B 树、B+树、R 树、多路树、红黑树 404 | 405 | 排序算法 406 | 407 | 各种排序算法和时间复杂度 深度优先和广度优先搜索 全排列、贪心算法、KMP 算法、hash 算法、海量数据处理 408 | 409 | 大数据知识 410 | 411 | Zookeeper 412 | 413 | 基本概念、常见用法 414 | 415 | Solr,Lucene,ElasticSearch 416 | 417 | 在 linux 上部署 solr,solrcloud,,新增、删除、查询索引 418 | 419 | Storm,流式计算,了解 Spark,S4 420 | 421 | 在 linux 上部署 storm,用 zookeeper 做协调,运行 storm hello world,local 和 remote 模式运行调试 storm topology。 422 | 423 | Hadoop,离线计算 424 | 425 | HDFS、MapReduce 426 | 427 | 分布式日志收集 flume,kafka,logstash 428 | 429 | 数据挖掘,mahout 430 | 431 | 网络安全知识 432 | 433 | 什么是 XSS 434 | 435 | XSS 的防御 436 | 437 | 什么是 CSRF 438 | 439 | 什么是注入攻击 440 | 441 | SQL 注入、XML 注入、CRLF 注入 442 | 443 | 什么是文件上传漏洞 444 | 445 | 加密与解密 446 | 447 | MD5,SHA1、DES、AES、RSA、DSA 448 | 449 | 什么是 DOS 攻击和 DDOS 攻击 450 | 451 | memcached 为什么可以导致 DDos 攻击、什么是反射型 DDoS 452 | 453 | SSL、TLS,HTTPS 454 | 455 | 如何通过 Hash 碰撞进行 DOS 攻击 456 | 457 | 用 openssl 签一个证书部署到 apache 或 nginx 458 | 459 | ## 四、架构篇 460 | 461 | 分布式 462 | 463 | 数据一致性、服务治理、服务降级 464 | 465 | 分布式事务 466 | 467 | 2PC、3PC、CAP、BASE、 可靠消息最终一致性、最大努力通知、TCC 468 | 469 | Dubbo 470 | 471 | 服务注册、服务发现,服务治理 472 | 473 | 分布式数据库 474 | 475 | 怎样打造一个分布式数据库、什么时候需要分布式数据库、mycat、otter、HBase 476 | 477 | 分布式文件系统 478 | 479 | mfs、fastdfs 480 | 481 | 分布式缓存 482 | 483 | 缓存一致性、缓存命中率、缓存冗余 484 | 485 | 微服务 486 | 487 | SOA、康威定律 488 | 489 | ServiceMesh 490 | 491 | Docker & Kubernets 492 | 493 | Spring Boot 494 | 495 | Spring Cloud 496 | 497 | 高并发 498 | 499 | 分库分表 500 | 501 | CDN 技术 502 | 503 | 消息队列 504 | 505 | ActiveMQ 506 | 507 | 监控 508 | 509 | 监控什么 510 | 511 | CPU、内存、磁盘 I/O、网络 I/O 等 512 | 513 | 监控手段 514 | 515 | 进程监控、语义监控、机器资源监控、数据波动 516 | 517 | 监控数据采集 518 | 519 | 日志、埋点 520 | 521 | Dapper 522 | 523 | 负载均衡 524 | 525 | tomcat 负载均衡、Nginx 负载均衡 526 | 527 | DNS 528 | 529 | DNS 原理、DNS 的设计 530 | 531 | CDN 532 | 533 | 数据一致性 534 | 535 | ## 五、 扩展篇 536 | 537 | 云计算 538 | 539 | IaaS、SaaS、PaaS、虚拟化技术、openstack、Serverlsess 540 | 541 | 搜索引擎 542 | 543 | Solr、Lucene、Nutch、Elasticsearch 544 | 545 | 权限管理 546 | 547 | Shiro 548 | 549 | 区块链 550 | 551 | 哈希算法、Merkle 树、公钥密码算法、共识算法、Raft 协议、Paxos 算法与 Raft 算法、拜占庭问题与算法、消息认证码与数字签名 552 | 553 | 比特币 554 | 555 | 挖矿、共识机制、闪电网络、侧链、热点问题、分叉 556 | 557 | 以太坊 558 | 559 | 超级账本 560 | 561 | 人工智能 562 | 563 | 数学基础、机器学习、人工神经网络、深度学习、应用场景。 564 | 565 | 常用框架 566 | 567 | TensorFlow、DeepLearning4J 568 | 569 | 其他语言 570 | 571 | Groovy、Python、Go、NodeJs、Swift、Rust 572 | 573 | ## 六、 推荐书籍 574 | 575 | 《深入理解 Java 虚拟机》 《 Effective Java 》 《深入分析 Java Web 技术内幕》 《大型网站技术架构》 《代码整洁之道》 《 Head First 设计模式》 《 maven 实战》 《区块链原理、设计与应用》 《 Java 并发编程实战》 《鸟哥的 Linux 私房菜》 《从 Paxos 到 Zookeeper 》 《架构即未来》 576 | 577 | ## 最后 578 | 579 | 这是很久以前摘录的一片有关 Java 开发的知识点,非本人创作,但是因为当时忘了记录出处,所以暂时不详。 -------------------------------------------------------------------------------- /iOS/Pattern_matching_in_Swift.md: -------------------------------------------------------------------------------- 1 | # Swift中的模式匹配 2 | 3 | *本文翻译自 [Pattern Matching in Swift](https://www.raywenderlich.com/134844/pattern-matching-in-swift),由 [Cosmin Pupaza](https://www.raywenderlich.com/u/shogunkaramazov) 发表于[Raywenderlich](https://www.raywenderlich.com)*。 4 | 5 | *受限于译者英语水平及翻译经验,译文难免有词不达意,甚至错误的地方,还望不吝赐教,予以指正*。 6 | 7 | ------------------- 8 | 9 | 10 | 11 | 12 | 对任何编程语言来说,模式匹配都是其最强大的特性之一,它允许你制定数值间匹配的规则,可使代码变得更加简洁和灵活。本文涉及以下模式: 13 | 14 | - 元组模式(Tuple pattern) 15 | - 类型转换模式(Type-casting patterns) 16 | - 通配符模式(Wildcard pattern) 17 | - 可选类型模式(Optional pattern) 18 | - 枚举转换模式(Enumeration case pattern) 19 | - 表达式模式(Expression pattern) 20 | 21 | 为了展示如何运用模式匹配,为你设计了如下场景:作为[raywenderlich.com](https://www.raywenderlich.com/)的主编,你需要通过模式匹配来排定教程并按照排定好的日程将其发布到网站上。 22 | 23 | 注意:该教程需要 Xcode 8 和 Swift 3,并假定您已具备 Swift 开发的基础知识。如果才开始接触Swift,请访问我们为您提供的其它[Swit教程](https://www.raywenderlich.com/category/swift)。 24 | 25 | ### 开始 26 | 27 | 首先下载[起始playground](https://cdn4.raywenderlich.com/wp-content/uploads/2016/07/starter-project.playground-2.zip)并打开。 28 | 29 | playground包含两部分: 30 | - `random_uniform(value:)`函数,会生成一个介于 0 和给定值之间的随机数,通过它来随机地为教程排定日期。 31 | - 余下的代码,通过解析**tutorials.json**文件,返回了一个包含字典元素的数组,接下来将通过该数组所包含信息对教程进行排期。 32 | 33 | 注意:欲了解更多在Swift中如何解析JSON的知识,请访问[教程](https://www.raywenderlich.com/120442/swift-json-tutorial) 34 | 35 | 虽然不需要了解解析的具体过程,但还是清楚其数据结构,在 playground 的 **Resources** 文件夹中找到并打开 **tutorials.json** 文件。可以看到每个待排定的教程均包含两个属性:title 和 day。每个排定的教程的 day 属性都会赋一个介于1(周一)到5(周五)的值,暂不排定的教程则设为 nil。 36 | 37 | 本想一周内每天一篇教程,但是查阅完教程安排表后发现,存在一天两篇教程的情况。你需要修复该问题,此外,还需对教程进行特定排序。该怎么解决这些问题呢?如果你能想到“用模式!”,那么你就算想对了。 38 | 39 | ### 模式匹配类型 40 | 41 | 让我们先来了解下本文即将用到的几种模式: 42 | 43 | - 元组模式(Tuple patterns)用于匹配正确的元组类型值 44 | - 类型转换模式(Type-casting patterns)允许你去转换或匹配类型 45 | - 通配符模式(Wildcard pattern)用于匹配或忽略任意值和类型 46 | - 可选类型模式(Optional pattern)用于匹配可选值 47 | - 枚举模式(Enumeration case pattern)用于匹配已有枚举类型 48 | - 表达式模式(Expression pattern)允许你通过一个指定的表达式去比较指定的值 49 | 50 | ### 元组模式(Tuple Pattern) 51 | 52 | 首先,通过元组模式创建一个包含所有教程的数组。在playground的最后添加如下代码: 53 | 54 | ``` Swift 55 | enum Day: Int { 56 | case monday, tuesday, wednesday, thursday, friday, saturday, sunday 57 | } 58 | ``` 59 | 这里一周的划分情况创建了一个原始类型为 **Int**的枚举,这样便可以通过赋 0 到 6 之间的任意值来表示星期一到星期日。在枚举的后面添加如下代码: 60 | 61 | ``` SWift 62 | class Tutorial { 63 | 64 | let title: String 65 | var day: Day? 66 | 67 | init(title: String, day: Day? = nil) { 68 | self.title = title 69 | self.day = day 70 | } 71 | } 72 | ``` 73 | 接下来通过一个数组来保存所有的 tutorial: 74 | 75 | ``` Swift 76 | var tutorials: [Tutorial] = [] 77 | ``` 78 | 79 | 紧挨着刚才的代码,添加如下内容,将包含字典的数组转换为包含 tutorial 对象的数组: 80 | 81 | ``` Swift 82 | for dictionary in json { 83 | var currentTitle = "" 84 | var currentDay: Day? = nil 85 | 86 | for (key, value) in dictionary { 87 | // todo: extract the information from the dictionary 88 | } 89 | 90 | let currentTutorial = Tutorial(title: currentTitle, day: currentDay) 91 | tutorials.append(currentTutorial) 92 | } 93 | ``` 94 | 95 | 这里通过`for-in`语句来遍历整个 json 数组,同时又通过元组去遍历字典中的键值对。这里展示了元组模式的应用。 96 | 97 | 已将 tutorial 添加到了数组中,但它现在还是空的,下一小节将通过类型转换模式来设置其属性。 98 | 99 | ### 类型转换模式(Type-Casting Patterns) 100 | 为了取出字典的信息,你将用到类型转换模式。在`for (key, value) in dictionary`循环体中,用如下代码替换掉注释: 101 | 102 | ``` Swift 103 | // 1 104 | switch (key, value) { 105 | // 2 106 | case ("title", is String): 107 | currentTitle = value as! String 108 | // 3 109 | case ("day", let dayString as String): 110 | if let dayInt = Int(dayString), let day = Day(rawValue: dayInt - 1) { 111 | currentDay = day 112 | } 113 | // 4 114 | default: 115 | break 116 | } 117 | ``` 118 | 119 | 代码说明: 120 | 121 | 1. 通过 switch 匹配元组中的键值对。 122 | 2. 测试 tutorial 的 title 是否为 String,如果是,则通过类型转换模式将其转换为 String 类型(译者注:这里有点String转String,但别迷糊,注意开始的 json 数组类型)。 123 | 3. 通过类型转换来测试day属性。如果测试通过,先将其转为整型,然后在通过 Day 枚举的可失败构造器 `init(rawValue:)`,将其构造成枚举 day。减 1 是因为 json 中的日期是从 1 开始, 而枚举是从 0 开始的。 124 | 4. switch语句要完整,这里添加一个 defalut 分支,并通过 break 语句来退出 switch。 125 | 126 | 在playground的最后添加如下代码,以将 tutorial 信息输出到控制台。 127 | 128 | ``` Swift 129 | print(tutorials) 130 | ``` 131 | 132 | 如你所见,现在数组中的每个 tutorial 都拥有了自己的 title 和 day 属性。 前期准备已毕,接下来就该解决教程排定的问题:一周中每天仅安排一个教程。 133 | 134 | ### 通配符模式(Wildcard Pattern) 135 | 136 | 要通过通配符模式来排定教程,首先得取消每个教程现有的日期安排。在 playground 的最后添如下代码: 137 | 138 | ``` Swift 139 | tutorials.forEach { $0.day = nil } 140 | ``` 141 | 142 | 这里通过将 tutorial 的 day 设置为 nil 来取消所有教程安排。为了重新排定所有教程,在 palayground 的最后添加如下代码: 143 | 144 | ``` Swift 145 | // 1 146 | let days = (0...6).map { Day(rawValue: $0)! } 147 | // 2 148 | let randomDays = days.sorted { _ in random_uniform(value: 2) == 0 } 149 | // 3 150 | (0...6).forEach { tutorials[$0].day = randomDays[$0] } 151 | ``` 152 | 153 | 代码说明: 154 | 155 | 1. 首先创建一个 days 数组,其中无重复日期。 156 | 2. 对数组进行“排序”. 通过`random_uniform(value:)`方法对数组中两个相邻元素进行随机排序。由于不需要,所以闭包中使用下划线来忽略参数。虽然还有其他更高效的数学方法来随机打乱一个数组,但这里更好地展示了通配符模式的应用。 157 | 3. 最后,将随机生成的日期赋值给前7个 tutorial 的 day 属性。 158 | 159 | 在 playground 的最后添加如下代码,将 tutorial 的排定情况输出到控制台: 160 | 161 | ``` Swift 162 | print(tutorials) 163 | ``` 164 | 165 | 成功了!现在你已经为一周中的每天安排了一个教程,无重复,无空白。做得好! 166 | 167 | ### 可选类型模式(Optional Pattern) 168 | 169 | 教程日程安排虽然搞定了,但是作为主编,你还需要对教程进行排序。接下来通过可选类型模式来解决该问题。 170 | 171 | 对教程进行升序排序,未排定日期的 tutorial 按 title 排序,已排定日期的 tutorial 按 day 进行排序。在 playground 的最后添加如下代码: 172 | 173 | ``` Swift 174 | // 1 175 | tutorials.sort { 176 | // 2 177 | switch ($0.day, $1.day) { 178 | // 3 179 | case (nil, nil): 180 | return $0.title.compare($1.title, options: .caseInsensitive) == .orderedAscending 181 | // 4 182 | case (let firstDay?, let secondDay?): 183 | return firstDay.rawValue < secondDay.rawValue 184 | // 5 185 | case (nil, let secondDay?): 186 | return true 187 | case (let firstDay?, nil): 188 | return false 189 | } 190 | } 191 | ``` 192 | 193 | 代码说明: 194 | 195 | 1. 通过数组的`sort(_:)`方法对 tutorials 进行排序。该方法接受一个简洁的闭包,该闭包定意义了数组中任一两个 tutorial 间的排序规则。升序该方法返回true,否则返回false。 196 | 197 | 2. switch接受一个元组,该元组包含两个当前正在进行比较的tutorial的da属性。这是元组模式的又一次应用。 198 | 199 | 3. 如果两个为排定的教程日期为nil,那么将通过 compare(_:options:) 方法,按教程名称对其进行升序排序。 200 | 201 | 4. 为了测试两个教程是否均已排定,这里使用了两个可选类型。 该模式将仅匹配哪些可以解封的值。如果两个值都可以被解封,那么通过它们的原始值进行升序排序 202 | 203 | 5. 同样运用可选模式,测试是否仅有一个教程被排定,如果是,则将未排定的教程排到前面。 204 | 205 | 添加改行代码到playground的最后,以打印排好序的教程: 206 | 207 | ``` Swift 208 | print(tutorials) 209 | ``` 210 | 211 | 现在我们已经将教程按预先的想法排好了。 212 | 213 | ### 枚举模式(Enumeration Case Pattern) 214 | 215 | 现在我们通过枚举模式去侦测每个教程具体安排的日期。 216 | 217 | 在 Tutorial 的 extension 中,你通过枚举来将 Day 类型转换为自定义的字符串。下面,添加一个计算型属性 name,来取代这种绑定关系。在Playground的最后添加如下代码: 218 | 219 | ``` Swift 220 | extension Day { 221 | 222 | var name: String { 223 | switch self { 224 | case .monday: 225 | return "Monday" 226 | case .tuesday: 227 | return "Tuesday" 228 | case .wednesday: 229 | return "Wednesday" 230 | case .thursday: 231 | return "Thursday" 232 | case .friday: 233 | return "Friday" 234 | case .saturday: 235 | return "Saturday" 236 | case .sunday: 237 | return "Sunday" 238 | } 239 | } 240 | } 241 | ``` 242 | 243 | 在 switch 语句通过枚举去匹配当前值(self)。这里展示了枚举类型在匹配中的应用。 244 | 245 | 一目了然,是不是?数字虽然计算便捷,但名称则更加直观且便于理解。 246 | 247 | ### 表达式模式(Expression Pattern) 248 | 249 | 接下来,会添加一个用于描述教程安排顺序的属性。你本可以向下面这样使在此使用枚举来处理该问题(不要将这里的代码添加到 Playground 中): 250 | 251 | ``` Swift 252 | var order: String { 253 | switch self { 254 | case .monday: 255 | return "first" 256 | case .tuesday: 257 | return "second" 258 | case .wednesday: 259 | return "third" 260 | case .thursday: 261 | return "fourth" 262 | case .friday: 263 | return "fifth" 264 | case .saturday: 265 | return "sixth" 266 | case .sunday: 267 | return "seventh" 268 | } 269 | } 270 | ``` 271 | 272 | 但同样事情做两次,是个差劲的主编,不是吗? 我们使用另外一种类似的方式来解决该问题。首先重载模式匹配操作符,改变其默认功能以便能适用于 Day 类型。在 playground 的最后添加如下代码: 273 | 274 | ``` Swift 275 | func ~=(lhs: Int, rhs: Day) -> Bool { 276 | return lhs == rhs.rawValue + 1 277 | } 278 | ``` 279 | 280 | 这段代码允许你使用 1 到 7 之间的任意整数与日期进行匹配。你可以通过该重载操作符以另外一种方式去实现你的计算型属性。 281 | 282 | 在palyground的最后添加如下代码: 283 | 284 | ``` Swift 285 | extension Tutorial { 286 | 287 | var order: String { 288 | guard let day = day else { 289 | return "not scheduled" 290 | } 291 | switch day { 292 | case 1: 293 | return "first" 294 | case 2: 295 | return "second" 296 | case 3: 297 | return "third" 298 | case 4: 299 | return "fourth" 300 | case 5: 301 | return "fifth" 302 | case 6: 303 | return "sixth" 304 | case 7: 305 | return "seventh" 306 | default: 307 | fatalError("invalid day value") 308 | } 309 | } 310 | } 311 | ``` 312 | 313 | 感谢模式匹配操作符重载,现在**day**对象已经可以跟整型表达式匹配了。这展示了表达式模式的应用。 314 | 315 | ### 组合起来 316 | 317 | 定义好了日期名称,并且教程的排序也排好了,现在开始打印每个教程。在 playground 的最后添加如下代码块: 318 | 319 | 320 | ``` Swift 321 | for (index, tutorial) in tutorials.enumerated() { 322 | guard let day = tutorial.day else { 323 | print("\(index + 1). \(tutorial.title) is not scheduled this week.") 324 | continue 325 | } 326 | print("\(index + 1). \(tutorial.title) is scheduled on \(day.name). It's the \(tutorial.order) tutorial of the week.") 327 | } 328 | ``` 329 | 330 | 注意到 for-in 语句中的元组了吗?,这里又用到了元组模式。 331 | 332 | ### 接下来 333 | 这里下载[最终playground](https://cdn2.raywenderlich.com/wp-content/uploads/2016/07/patterns.playground-1.zip)。想亲自试试,可以到 [IBM Swift Sandbox](http://swiftlang.ng.bluemix.net/#/repl/57b2d2a68bbb01d512f4ca2b) 动手体验。 334 | 335 | 如果想了解更多Swift中关于模式匹配的知识,请访问 [Greg Heo](https://twitter.com/gregheo) 在[RWDevCon 2016](https://twitter.com/RWDevCon)上的[视频](https://www.raywenderlich.com/132574/rwdevcon-2016-session-202-programming-swift-style-2)**Programming in a Swift Style**。 -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_objc_runtime.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之Runtime 2 | 3 | ## 1. 运行时基础 4 | ### 1.1 运行时的载入过程如何 5 | 6 | 1. read all classes in all new images, add them all to unconnected_hashl; 7 | 2. read all categories in all new images, 8 | 3. try to connected all classes 9 | 4. resolve selector refs and class refs 10 | 5. fix up protocol object 11 | 6. call `+load()` for classes and categories 12 | 7. all classes are ready before any categories are ready. 13 | 14 | ### 1.2 load 和 initialize 方法的调用时机? 15 | 16 | - load 当类被加载到 runtime 的时候运行,在 main 函数执行之前,也就是类加载器加载时,每个类默认只会调用一次;通常被用来进行 Method Swizzle,但是这会增加 App 启动时间。 17 | - initialize 在类接收到第一条消息之前被用调用。每个类只会调用一次。子类未实现会向上查找。一搬用来初始化全局变量或静态变量。 18 | 19 | ### 1.3 ISA指针 20 | isa 指针实际上是一个联合体,其结构如下: 21 | 22 | ```cpp 23 | // 精简过的isa_t共用体 24 | union isa_t 25 | { 26 | isa_t() { } 27 | isa_t(uintptr_t value) : bits(value) { } 28 | 29 | Class cls; 30 | uintptr_t bits; 31 | 32 | # if __arm64__ 33 | # define ISA_MASK 0x0000000ffffffff8ULL 34 | # define ISA_MAGIC_MASK 0x000003f000000001ULL 35 | # define ISA_MAGIC_VALUE 0x000001a000000001ULL 36 | struct { 37 | // 0代表普通的指针,存储着Class,Meta-Class对象的内存地址。 38 | // 1代表优化后的使用位域存储更多的信息。 39 | uintptr_t nonpointer : 1; 40 | // 是否有设置过关联对象,如果没有,释放时会更快 41 | uintptr_t has_assoc : 1; 42 | // 是否有C++析构函数,如果没有,释放时会更快 43 | uintptr_t has_cxx_dtor : 1; 44 | // 存储着Class、Meta-Class对象的内存地址信息 45 | uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 46 | // 用于在调试时分辨对象是否未完成初始化 47 | uintptr_t magic : 6; 48 | // 是否有被弱引用指向过。 49 | uintptr_t weakly_referenced : 1; 50 | // 对象是否正在释放 51 | uintptr_t deallocating : 1; 52 | // 引用计数器是否过大无法存储在isa中 53 | // 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中 54 | uintptr_t has_sidetable_rc : 1; 55 | // 里面存储的值是引用计数器减1 56 | uintptr_t extra_rc : 19; 57 | 58 | # define RC_ONE (1ULL<<45) 59 | # define RC_HALF (1ULL<<18) 60 | }; 61 | #endif 62 | }; 63 | ``` 64 | 65 | ## 2. 类别(category) 66 | 67 | ### 2.1 什么是 category 68 | 下面是 category 的结构体: 69 | 70 | ```cpp 71 | typedef struct category_t *Category; 72 | 73 | struct category_t { 74 | const char *name; 75 | classref_t cls; 76 | struct method_list_t *instanceMethods; 77 | struct method_list_t *classMethods; 78 | struct protocol_list_t *protocols; 79 | struct property_list_t *instanceProperties; 80 | // Fields below this point are not always present on disk. 81 | struct property_list_t *_classProperties; 82 | 83 | method_list_t *methodsForMeta(bool isMeta) { 84 | if (isMeta) return classMethods; 85 | else return instanceMethods; 86 | } 87 | 88 | property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi); 89 | }; 90 | ``` 91 | 92 | category 是 Objective-C 2.0 之后添加的语言特性,主要作用是为已经存在的类追加方法。此外 Apple 还推荐另外两个使用场景: 93 | 94 | 1. 可以把类的实现分开在几个不同的文件里,这样可以减少单个文件的体积;可以对功能进行分类,组织到不同的 category 中;可由多人共同开发一个类,减少冲突;按需加载,方便配置; 95 | 2. 声明私有方法; 96 | 97 | ### 2.2 category 的特点 98 | - category 只能给某个已有的类追加方法,不能追加成员变量 99 | - category 可以追加属性,但只能生成 setter 和 getter 的声明,不能生成 setter 和 getter 的实现; 100 | - 如果 category 中的方法和类已有方法同名,category 中的方法会“覆盖”掉类中的已有方法(并不是真的覆盖,而是插入到原有方法之前,所以会被优先查询并返回); 101 | - 如果多个 category 中存在同名的方法啊,因为运行时加载时添加的顺序是无法保证的,所以“覆盖”的顺序也就不能确定,进而导致最终不知道会调用哪个。 102 | 103 | ### 2.3 category VS extension 104 | 105 | - extension 运行在编译期,它是类的一部分,拓展的方法,属性和变量一起形成一个完整的类。而 category 是运行期决定的,此时对象的内存布局已经确定,无法再追加变量 106 | - extension 一般用来隐藏类的私有信息,也就说你只能给已有源码的类添加 extension,而 category 则不存在该问题; 107 | - category 不能添加成员变量,而 extension 则没这个限制。 108 | 109 | ### 2.4 category 原理 110 | 分类的实现原理是将category中的方法,属性,协议数据放在category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中。 111 | Category可以添加属性,但是并不会自动生成成员变量及set/get方法。因为category_t结构体中并不存在成员变量。通过之前对对象的分析我们知道成员变量是存放在实例对象中的,并且编译的那一刻就已经决定好了。而分类是在运行时才去加载的。那么我们就无法再程序运行时将分类的成员变量中添加到实例对象的结构体中。因此分类中不可以添加成员变量 112 | 113 | > 引自掘金 xx_cc 的[《Category 的本质》](https://juejin.im/post/5aef0a3b518825670f7bc0f3) 114 | 115 | ### 2.5 category 不能添加成员变量,那么如果要添加有什么办法吗? 116 | 有,两种方式: 117 | 118 | - 利用静态变量,category 可以添加属性,通过属性来操作该静态变量。但是这样,即便类被销毁了,该静态变量也持续存在,且依然保留原有的值; 119 | - 利用关联对象 Associate Object; 120 | 121 | ### 2.6 如何使用关联对象呢? 122 | ```objc 123 | -(void)setName:(NSString *)name 124 | { 125 | objc_setAssociatedObject(self, @"name",name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 126 | } 127 | -(NSString *)name 128 | { 129 | return objc_getAssociatedObject(self, @"name"); 130 | } 131 | ``` 132 | 添加属性: 133 | 134 | ```objc 135 | objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); 136 | ``` 137 | 关联对象的属性设置: 138 | 139 | ```objc 140 | typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { 141 | OBJC_ASSOCIATION_ASSIGN = 0, // 指定一个弱引用相关联的对象 142 | OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相关对象的强引用,非原子性 143 | OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // 指定相关的对象被复制,非原子性 144 | OBJC_ASSOCIATION_RETAIN = 01401, // 指定相关对象的强引用,原子性 145 | OBJC_ASSOCIATION_COPY = 01403 // 指定相关的对象被复制,原子性 146 | }; 147 | ``` 148 | 获取属性: 149 | 150 | ```objc 151 | objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); 152 | ``` 153 | 移除属性: 154 | 155 | ```objc 156 | - (void)removeAssociatedObjects 157 | { 158 | // 移除所有关联对象 159 | objc_removeAssociatedObjects(self); 160 | } 161 | 162 | ``` 163 | ### 2.7 关联对象原理 164 | 一个实例对象就对应一个 ObjectAssociationMap,而ObjectAssociationMap 中存储着多个此实例对象的关联对象的 key 以及 ObjcAssociation,为 ObjcAssociation 中存储着关联对象的 value 和 policy 策略。 165 | 由此我们可以知道关联对象并不是放在了原来的对象里面,而是自己维护了一个全局的map用来存放每一个对象及其对应关联属性表格 166 | 167 | ![关联对象原理](https://user-gold-cdn.xitu.io/2018/5/14/1635a628a228e349?imageView2/0/w/1280/h/960/ignore-error/1) 168 | > 引自掘金 cc_xx [《关联对象实现原理》](https://juejin.im/post/5af86b276fb9a07aa34a59e6) 169 | 170 | ### 2.8 使用 runtime Associate 方法关联的对象,需要在主对象 dealloc 的时候释放吗? 171 | 无论在 MRC 下还是 ARC 下均不需要在主对象 dealloc 的时候释放,被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被 NSObject -dealloc 调用的 object_dispose() 方法中释放。 172 | 173 | ### 2.9 关联属性如何显示 weak ? 174 | 利用中间对象包装一层???? 175 | 176 | ```objc 177 | -(void)setWeakvalue:(NSObject *)weakvalue { 178 | __weak typeof(weakvalue) weakObj = weakvalue; 179 | typeof(weakvalue) (^block)() = ^(){ 180 | return weakObj; 181 | }; 182 | objc_setAssociatedObject(self, weakValueKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC); 183 | } 184 | -(NSObject *)weakvalue { 185 | id (^block)() = objc_getAssociatedObject(self, weakValueKey); 186 | return block(); 187 | } 188 | ``` 189 | 190 | ## 3. 消息转发(objc_mgSend) 191 | ![方法调用的流程](https://user-gold-cdn.xitu.io/2018/7/2/16456e432e51af79?imageView2/0/w/1280/h/960/ignore-error/1) 192 | 193 | ### 3.1 为什么 Objective—C 的方法不叫调用叫发消息? 194 | 因为 Objective-C 中的方法调用其实都是转成了 objc_msgSend 函数的调用,给 receiver(方法调用者)发送了一条消息(selector方法名)。方法调用过程中也就是 objc_msgSend 底层实现分为三个阶段:消息发送、动态方法解析、消息转发 195 | 196 | ``` 197 | id _Nullable objc_msgSend(id _Nullable self, SEL _Nonnull op, ...) 198 | ``` 199 | > ⚠️注意参数 200 | 201 | ### 3.2 给一个对象发消息的过程如何,或者说如何通过 selector 找到对应的 IMP 地址: 202 | 203 | 1. 先在 cache 中查找 204 | 2. 在方法类表中查找 205 | 3. 到父类的 cache,方法列表中查找 206 | 4. 查遍所有直到根类 207 | 208 | ### 3.3 类对象也是如此吗: 209 | 是的,只是实例对象要先通过 isa 指针取得该实例的类,然后就一样了。如果是类方法,因为类方法定义在原类中,则先通过 ISA 指针得到元类,之后就一样了。 210 | 211 | ### 3.4 如果消息发送失败有哪些补救措施: 212 | 213 | - 动态解析:你不能处理啊,那你要添加一个的吗?实例方法调用 `+(BOOL)resolveInstanceMethod:(SEL)selector`,如果是类方法,那么调用 `+(BOOL)resolveClassMethod:(SEL)selector`,如果要动态添加,就重写响应方法,在方法实现中添加,并返回 YES, 如果不,要记得调用 super; 214 | - 消息转发:添加不了啊,那要我转发给别人吗?`-(id)forwardingTargetForSelector:(SEL)selector`,如果有备用处理对象,那么在此返回,否则返回 nil。通过此方法,可以配合 composition(在对象内部封入子对象,将任务交给子对象处理) 来模拟“多重继承”的某些特性。 215 | - 全部内容都在这里了(NSInvocation),你看着办吧。`-(void)forwardInvocation:(NSInvocation *)invocation`,此方法可以实现的很简单,只需该面调用目标,使消息在新目标上得以调用即可。这就与上一步类似了。还可以在触发消息前,先以某种方式改变消息内容,比如追加另外一个参数,或者该换选择子(selector),等等。 216 | 217 | 接受者在以上的每一步都有机会处理消息,越往后代价越大。最好能在第一步就处理完毕,这样的话,运行时可以将此方法缓存起来。 218 | 219 | ### 消息转发哪些步骤可以被利用 220 | 消息转发时可以结合组合来模拟多重继承的某些特性。 221 | 222 | ## 4. KVO 223 | Key-value Observing,即键值监听,可以用于监听某个对象属性的变化。 224 | 225 | ### 4.1 KVO 的基本原理 226 | 先上结论:利用 Runtime 动态创建一个支持 KVO 的子类,修改该实例对象的 `isa` 指针,让其指向刚刚创建的子类,从而实现 KVO。 227 | 228 | 下面来看一下具体过程如何。假设我们有一个 `Person` 的实例对象,那么它查找方法的过程如下图所示: 229 | ![原实例方法查找过程](https://user-gold-cdn.xitu.io/2018/4/21/162e65aff55e142f?imageView2/0/w/1280/h/960/ignore-error/1) 230 | 一旦我们通过下面的方法: 231 | 232 | [person addObserver: forKeyPath: options: context:] 233 | 向实例对象 `person` 添加监听,Runtime 便动态生成一个 `Person` 类的子类 `NSKVONotifying_Person `,这个子类会重写 `setter`、`class`、`dealloc` 以及 `_isKVOA` 等相关方法(之所以要重写 `Class` 方法是为了尽量保持与原类相同,避免因为替换了类的实现而导致异常)。并修改被监听实例的 `isa` 指针,让其指向这个新生成的子类 `NSKVONotifying_Person`。因为这个新增的子类实现了 KVO 的相关方法,所以当我们再对该实例发送消息时,会通过 `isa` 指针先到这个新的子类中查找相应方法,从而得到通知。 234 | 235 | ![动态子类的方法查找过程](https://user-gold-cdn.xitu.io/2018/4/21/162e65b0293d6569?imageView2/0/w/1280/h/960/ignore-error/1) 236 | 237 | 同理,当我们调用下面的方法: 238 | 239 | [Object removeObserver: forKeyPath:] 240 | 移除一个对象实例的监听时,Runtime 会将该实例的 `isa` 指针恢复,同时移除生成的子类。 241 | 242 | > 关于销毁 KVO 的动态子类还有待进一步研究 243 | 244 | ### 4.2 KVO 在多线程中的行为如何? 245 | - KVO 是同步的,一旦对象的属性发生变化,只有用同步的方式,才能保证所有观察者的方法能够执行完成。KVO 监听方法中,不要有太耗时的操作。 246 | 247 | - KVO 的方法调用,是在对应的线程中执行的。在子线程修改观察属性时,观察者回调方法将在子线程中执行。 248 | 249 | - 在多个线程同时修改一个观察属性的时候,KVO 监听方法中会存在资源抢夺的问题,需要使用互斥锁。如果涉及到多线程,KVO 要特别小心,通常 KVO 只是做一些简单的观察和处理,千万不要搞复杂了,KVO的监听代码,一定要简单。 250 | 251 | [参考](http://www.cnblogs.com/QianChia/p/5771074.html) 252 | 253 | ### 4.3 如何手动触发 KVO? 254 | 首先关闭默认: 255 | 256 | ``` Objective-C 257 | + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key 258 | { 259 | 260 | if ([key isEqualToString:@"想要手动控制的key"])return NO; 261 | 262 | return [super automaticallyNotifiesObserversForKey:key]; 263 | } 264 | ``` 265 | 然后重写 setter : 266 | 267 | ``` Objective-C 268 | - (void)setTmpStr:(NSString *)tmpStr 269 | { 270 | [self willChangeValueForKey:@"tmpStr"]; 271 | 272 | _tmpStr = tmpStr; 273 | 274 | [self didChangeValueForKey:@"tmpStr"]; 275 | } 276 | ``` 277 | 278 | ## 10. 其它 279 | ### 10.1 如何访问并修改一个类的私有属性 280 | 281 | 1. 通过 KVC 获取 282 | 2. 通过 runtime 访问并修改私有属性 283 | 284 | ### 10.2 weak实现机制?为什么对象释放后会自动置为nil? 285 | 运行时会维护一张 weak 哈希表, weak 对象的地址作为 key,该表记录了每个对象的引用计数,当计数为 0 时就会触发销毁机制, 286 | 287 | 288 | ### 10.3 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么? 289 | 290 | 1. 不可以,因为结构的偏移已经固定了 291 | 2. 可以,这是新建,当然可以声明并定义了 -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_data_structures.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之数据结构 2 | 3 | ## 1. 基础数据结构的实现 4 | ### 1.1 可变数组的实现 5 | 6 | ```c 7 | #include 8 | #include 9 | #include "MutableArray.h" 10 | 11 | 12 | // 创建数组 13 | Array array_create(int init_size) { 14 | Array a; 15 | a.array = (int *)malloc(sizeof(int) * init_size); 16 | a.size = init_size; 17 | return a; 18 | } 19 | 20 | // 释放数组的内存空间 21 | void array_free(Array *a) { 22 | free(a->array); 23 | a->array = NULL; 24 | a->size = 0; 25 | } 26 | 27 | // 剩余可用空间 28 | int array_size(Array *a) { 29 | return a->size; 30 | } 31 | 32 | // 访问数组中某个元素,可读可写 33 | int * array_at(Array *a, int index) { 34 | if (index >= a->size) { 35 | array_inflate(a, (index / BLOCK_SIZE + 1) * BLOCK_SIZE - a->size) 36 | } 37 | return &(a->array[index]); 38 | } 39 | 40 | // 数组扩容 41 | void array_inflate(Array *a, int more_size) { 42 | int *p = (int *)malloc(sizeof(int) * (a->size + more_size)); 43 | int i; 44 | // 将原空间的内容全部拷贝至新空间 45 | for ( i = 0; i < a->size; ++i) 46 | { 47 | p[i] = a->array[i]; 48 | } 49 | 50 | // array_free(a); 51 | free(a->array); 52 | a->array = p; 53 | a->size += more_size; 54 | } 55 | 56 | // 向数组中写入 57 | void array_set(Array *a, int index, int value) { 58 | a->array[index] = value; 59 | } 60 | ``` 61 | 引自 [用C语言简单实现一个可变数组](https://blog.csdn.net/melody_1016/article/details/81948809) 62 | ### 1.2 如何实现一个可变的哈希表/等同于如何实现一个 MutilableDictionary? 63 | 64 | 哈希表的难点在于如何将键与值的位置映射起来,以及如何解决哈希碰撞问题 65 | 66 | ```swift 67 | public struct HashTable { 68 | private typealias Element = (key: Key, value: Value) 69 | private typealias Bucket = [Element] 70 | private var buckets: [Bucket] 71 | 72 | /// 哈希表中的键值对数量. 73 | private(set) public var count = 0 74 | 75 | /// 标识哈希表是否为空 76 | public var isEmpty: Bool { return count == 0 } 77 | 78 | /** 79 | 创建一个指定容量的哈希表 80 | */ 81 | public init(capacity: Int) { 82 | assert(capacity > 0) 83 | buckets = Array(repeatElement([], count: capacity)) 84 | } 85 | 86 | /** 87 | 下标方法:通过指定的键读/写相应的值 88 | */ 89 | public subscript(key: Key) -> Value? { 90 | get { 91 | return value(forKey: key) 92 | } 93 | set { 94 | if let value = newValue { 95 | updateValue(value, forKey: key) 96 | } else { 97 | removeValue(forKey: key) 98 | } 99 | } 100 | } 101 | 102 | /** 103 | 取值方法 104 | */ 105 | public func value(forKey key: Key) -> Value? { 106 | let index = self.index(forKey: key) 107 | for element in buckets[index] { 108 | if element.key == key { 109 | return element.value 110 | } 111 | } 112 | return nil // 不存在返空 113 | } 114 | 115 | /** 116 | 根据指定的键更新或者添加一个值. 117 | */ 118 | @discardableResult public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { 119 | let index = self.index(forKey: key) 120 | 121 | // 是否已经存在? 122 | for (i, element) in buckets[index].enumerated() { 123 | if element.key == key { 124 | let oldValue = element.value 125 | buckets[index][i].value = value 126 | return oldValue 127 | } 128 | } 129 | 130 | // 如果不存在,添加到链中. 131 | buckets[index].append((key: key, value: value)) 132 | count += 1 133 | return nil 134 | } 135 | 136 | /** 137 | 根据指定的键移除响应的值. 138 | */ 139 | @discardableResult public mutating func removeValue(forKey key: Key) -> Value? { 140 | let index = self.index(forKey: key) 141 | 142 | // Find the element in the bucket's chain and remove it. 143 | for (i, element) in buckets[index].enumerated() { 144 | if element.key == key { 145 | buckets[index].remove(at: i) 146 | count -= 1 147 | return element.value 148 | } 149 | } 150 | return nil // 不存在则返空 151 | } 152 | 153 | /** 154 | 移除所有键值对 155 | */ 156 | public mutating func removeAll() { 157 | buckets = Array(repeatElement([], count: buckets.count)) 158 | count = 0 159 | } 160 | 161 | /** 162 | 返回指定键的数组索引 163 | */ 164 | private func index(forKey key: Key) -> Int { 165 | return abs(key.hashValue) % buckets.count 166 | } 167 | } 168 | 169 | /// 该扩展是选择性的,但是有更方便。 170 | extension HashTable: CustomStringConvertible { 171 | 172 | public var description: String { 173 | let pairs = buckets.flatMap { b in b.map { e in "\(e.key) = \(e.value)" } } 174 | return pairs.joined(separator: ", ") 175 | } 176 | 177 | public var debugDescription: String { 178 | var str = "" 179 | for (i, bucket) in buckets.enumerated() { 180 | let pairs = bucket.map { e in "\(e.key) = \(e.value)" } 181 | str += "bucket \(i): " + pairs.joined(separator: ", ") + "\n" 182 | } 183 | return str 184 | } 185 | } 186 | ``` 187 | 引自 [Swift Algorithm club](https://github.com/raywenderlich/swift-algorithm-club) by [raywenderlich](https://www.raywenderlich.com) 188 | ### 1.3 实现一个栈 189 | 190 | 栈比较简单,只要对可变数组做些限制就可以了 191 | 192 | ```swift 193 | struct Stack { 194 | var elements = [E]() 195 | 196 | mutating func push(_ e: E) { 197 | elements.append(e) 198 | } 199 | 200 | mutating func pop() -> E? { 201 | return elements.popLast() 202 | } 203 | } 204 | 205 | extension Stack { 206 | var topElement: E? { 207 | return elements.last 208 | } 209 | } 210 | 211 | extension Stack: CustomStringConvertible { 212 | 213 | var description: String { 214 | return elements.description 215 | } 216 | } 217 | ``` 218 | ### 1.4 实现一个队列 219 | 220 | 通过数组实现的队列,重点在于如何优化解决出队时,数组后续元素整体迁移的问题,从而降低时间复杂度 221 | 222 | ```swift 223 | public struct Queue { 224 | fileprivate var array = [T?]() 225 | fileprivate var head = 0 226 | 227 | public var isEmpty: Bool { 228 | return count == 0 229 | } 230 | 231 | public var count: Int { 232 | return array.count - head 233 | } 234 | 235 | public mutating func enqueue(_ element: T) { 236 | array.append(element) 237 | } 238 | 239 | public mutating func dequeue() -> T? { 240 | guard head < array.count, let element = array[head] else { return nil } 241 | 242 | array[head] = nil 243 | head += 1 244 | 245 | let percentage = Double(head)/Double(array.count) 246 | if array.count > 50 && percentage > 0.25 { 247 | array.removeFirst(head) 248 | head = 0 249 | } 250 | 251 | return element 252 | } 253 | 254 | public var front: T? { 255 | if isEmpty { 256 | return nil 257 | } else { 258 | return array[head] 259 | } 260 | } 261 | } 262 | ``` 263 | 引自 [Swift Algorithm club](https://github.com/raywenderlich/swift-algorithm-club) by [raywenderlich](https://www.raywenderlich.com) 264 | ### 1.5 实现一个LRU缓存 265 | 266 | ```swift 267 | import Foundation 268 | 269 | public class LRUCache { 270 | private let maxSize: Int 271 | private var cache: [KeyType: Any] = [:] 272 | private var priority: LinkedList = LinkedList() 273 | private var key2node: [KeyType: LinkedList.LinkedListNode] = [:] 274 | 275 | public init(_ maxSize: Int) { 276 | self.maxSize = maxSize 277 | } 278 | 279 | public func get(_ key: KeyType) -> Any? { 280 | guard let val = cache[key] else { 281 | return nil 282 | } 283 | 284 | remove(key) 285 | insert(key, val: val) 286 | 287 | return val 288 | } 289 | 290 | public func set(_ key: KeyType, val: Any) { 291 | if cache[key] != nil { 292 | remove(key) 293 | } else if priority.count >= maxSize, let keyToRemove = priority.last?.value { 294 | remove(keyToRemove) 295 | } 296 | 297 | insert(key, val: val) 298 | } 299 | 300 | private func remove(_ key: KeyType) { 301 | cache.removeValue(forKey: key) 302 | guard let node = key2node[key] else { 303 | return 304 | } 305 | priority.remove(node: node) 306 | key2node.removeValue(forKey: key) 307 | } 308 | 309 | private func insert(_ key: KeyType, val: Any) { 310 | cache[key] = val 311 | priority.insert(key, atIndex: 0) 312 | guard let first = priority.first else { 313 | return 314 | } 315 | key2node[key] = first 316 | } 317 | } 318 | ``` 319 | 320 | 引自 [Swift Algorithm club](https://github.com/raywenderlich/swift-algorithm-club) by [raywenderlich](https://www.raywenderlich.com) 321 | 322 | ## 2. 常用类型的数据结构 323 | ### 2.1 图片在内存的中的数据结构是什么样的? 324 | 325 | 二维数组,第一维度是每个元素代表一个像素,第二维度的每个元素代表一个像素的信息,通常是颜色空间,即色域,常见有的 YUV、RGB、RGBA 等。 326 | 327 | ## 3. 常见底层函数的实现 328 | ### 3.1 malloc 函数如何实现的 329 | 330 | 初始自己真实太天真,知道看见这个详解介绍。 331 | 332 | ```c 333 | #define BLOCK_SIZE 24 334 | void *first_block=NULL; 335 | 336 | /* other functions... */ 337 | 338 | void *malloc(size_tsize){ 339 | t_blockb,last; 340 | size_ts; 341 | /* 对齐地址 */ 342 | s= align8(size); 343 | if(first_block){ 344 | /* 查找合适的block */ 345 | last= first_block; 346 | b= find_block(&last,s); 347 | if(b){ 348 | /* 如果可以,则分裂 */ 349 | if((b->size- s)>= (BLOCK_SIZE +8)) 350 | split_block(b,s); 351 | b->free= 0; 352 | }else { 353 | /* 没有合适的block,开辟一个新的 */ 354 | b= extend_heap(last,s); 355 | if(!b) 356 | returnNULL; 357 | } 358 | }else { 359 | b= extend_heap(NULL,s); 360 | if(!b) 361 | returnNULL; 362 | first_block= b; 363 | } 364 | returnb->data; 365 | } 366 | 367 | ``` 368 | 369 | 370 | 引自 [malloc 函数实现原理](https://blog.csdn.net/yeditaba/article/details/53443792) 371 | 372 | #### 参考 373 | 374 | [浅析malloc()的几种实现方式](http://lionwq.spaces.eepw.com.cn/articles/article/item/18555) 375 | 376 | [Anatomy of a Program in Memory](https://manybutfinite.com/post/anatomy-of-a-program-in-memory/) 377 | 378 | [How The Kernel Manages Your Memory](https://manybutfinite.com/post/how-the-kernel-manages-your-memory/) 379 | 380 | [GNU的malloc.c](https://repo.or.cz/w/glibc.git/blob/HEAD:/malloc/malloc.c) -------------------------------------------------------------------------------- /iOS/Swift_Tips.md: -------------------------------------------------------------------------------- 1 | # 【整理】Swift 学习小记 2 | *本文主要记录一些自己在Swift的学习过程中遇到的一些小知识点,以便加深记忆* 3 | 4 | [TOC] 5 | 6 | ## 1. 如何给代码分段 7 | 熟悉Objective-C的同学都知道可以通过`#pragma mark escription` 宏标记,在代码的方法导航中添加描述语句。此外,还可以在描述语句前添加一条中划线,例如`#pragma mark - Description`,这样在方法导航中,会显示一条横线将代码段分隔开,令分段显示更加清晰。 8 | 9 | 那么怎么在Swift中使用呢?可以通过`//MARK: description`,`//????: description `,`//FIXME: description `这样的注释来添加分段说明,同样的,也可以在冒号后添加中划线,例如:`//MARK: - description`,`//????: - description `,`//FIXME: - description `,将代码分段用横线隔开。 10 | 11 | ## 2.如何在Swift中使用保留关键字 12 | 通常一种编程语言都是不允许使用关键字作为变量名的,那么在SWift中如果非要用到关键字怎么办呢?可以通过用\`\`将关键字括起的方式来使用,例如:\`self\`,当然,这是最后的手段,但有一线希望都不应该用关键字来作为变量名。 13 | 14 | ## 3.几个常见数值字面量 15 | - 十进制 `let decimalInteger = 17` 16 | - 二进制 `let binaryInterger = 0b10001` 等价于17 17 | - 八进制 `let octalInteger = 0o21` 等价于17 18 | - 十六进制 `let hexadecimalInteger = 0x11` 等价于17 19 | - 科学计数法 `1.25e2` 等价于`125.0`;`1.25e-2`等价于`0.0125` 20 | - 十六进制指数式 `0xFp2`表示$15 * 2^2$,`0xFp-2`表示$15 * 2^-2$ 21 | - 十进制格式增强 `let paddedDobule = 000123.456`,`let oneMillion = 1_000_000`,`let justOverOneMillion = 1_000_000.000_000_1` 22 | 23 | ## 4.lazy属性的线程安全问题 24 | 我们知道 Swift 通过其语言自身的特点,保证了全局常量和存储型属性,即使被多个线程交替存取,也仅初始化一次,但是这里有一个特例,就是 lazy 属性,当多个线程同时访问一个尚未初始化的 lazy 属性时,则不能保证仅初始化一次。 25 | 26 | ## 5.如何便捷的在Swift中获取指针 27 | Swift虽然在极力避免指针,但是为了 Object-C 和 C 兼容,还是保留了指针,但是我们不能像在 C 中一样通过`&`便捷的获取某个常量或变量的指针,例如: 28 | 29 | ``` Swift 30 | let a = 8 31 | 32 | let b = &a //编译错误 33 | 34 | ``` 35 | 查看下错误信息:"Type ‘inout Int’ of variable is not materializable",这里编译器将`&a`识别为 inout Int类型,并不是我们期望的指针类型,所以是不是我们只要把它明确指定为指针类型就可以了呢?我们来试一下: 36 | 37 | ``` Swift 38 | func converToUnsafePointer(_ pointer: UnsafePointer) -> UnsafePointer { 39 | return pointer 40 | } 41 | 42 | func converToUnsafeMutablePointer(_ pointer: UnsafeMutablePointer) -> UnsafeMutablePointer { 43 | return pointer 44 | } 45 | 46 | var a = 8 47 | 48 | var b = converToUnsafePointer(&a) 49 | 50 | var c = converToUnsafeMutablePointer(&a)//顺利通过编译 51 | ``` 52 | 哈哈,成功了,顺利通过编译。 53 | 54 | ## 6.Swift错误处理原则 55 | *来源:[Magical Error Handling in Swift](https://www.raywenderlich.com/130197/magical-error-handling-swift),由 [Gemma Barlow](https://www.raywenderlich.com/u/gemmakbarlow) 发表于Raywenderliche* 56 | 57 | - 错误类型的命名要清晰无歧义 58 | - 单个错误尽量用可选类型来处理 59 | - 当可能出现多种错误时,用自定义的错误类型来处理 60 | - 不要让抛除的错误传播过远 61 | 62 | ## 7.Swift数组指针的妙用 63 | 64 | ``` Swift 65 | let numbers = [1, 2, 3, 4, 5] 66 | let sum = numbers.withUnsafeBufferPointer { buffer -> Int in 67 | var result = 0 68 | for i in stride(from: buffer.startIndex, to: buffer.endIndex, by: 2) { 69 | result += buffer[i] 70 | } 71 | return result 72 | } 73 | // 'sum' == 9 74 | ``` 75 | 76 | ## 8.简写闭包参数名 77 | *来源《The Swift Programming Language (SWift 3.0.1)》Closure 一章Shorthand Argument Names 小节* 78 | 79 | Swift 自动为内联闭包提供了依次代表代表参数值的`$0`,`$1`,`$2`等参数简写。 80 | 81 | 如果您在闭包表达式中使用参数名称简写,您可以在定义闭包时省略其参数列表,相应参数简写的类型会通过函数类型对其进行推断。此外`in`关键字也可以被省略,因为此时闭包表达式完全由闭包函数体构成: 82 | 83 | ``` Swift 84 | reversed = sorted(names, { $0 > $1 } ) 85 | 86 | ``` 87 | 在这个例子中,`$0`和`$1`分别表示闭包中第一个和第二个String类型的参数。 88 | 89 | ## 9.绘制虚线 90 | 91 | ```Swift 92 | if let context = UIGraphicsGetCurrentContext() { 93 | CGContextSetLineWidth(context, 2.0); 94 | CGContextSetStrokeColorWithColor(context, UIColor.greenColor().CGColor); 95 | CGContextBeginPath(context); 96 | 97 | // 该数组用于指定虚线的重复样式,这里指定绘制10,然后跳过5,依次重复。 98 | let lengths: [CGFloat] = [10.0, 5.0] 99 | // 第4个参数指定传入的数组包含有几个元素。 100 | CGContextSetLineDash(context, 0.0, lengths, 2) 101 | 102 | CGContextMoveToPoint(context, 0, rect.size.height/2) 103 | CGContextAddLineToPoint(context, rect.width, rect.size.height/2) 104 | CGContextStrokePath(context) 105 | } 106 | ``` 107 | 108 | ## 10.访问控制 109 | Swift 3.0更新了访问控制,添加了open和fileprivate两个新控制权限,这里摘录了[《The Swift Programming Language (Swift 3)》](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-ID3)书中部分内容: 110 | 111 | Swift 为代码中的实体提供了5种不同的访问权限。访问权限同时取决于源文件中的实体定义及源文件所属模块。 112 | 113 | - open、public 访问权限定义的实体,在同一模块内可于任意源文件内访问,即使该源文件属于另一个被引入的模块。open 和 public 的区别稍后介绍。 114 | - internal 访问权限定义的实体,可以被其定义的模块内的任意源文件访问,模块外的源文件则不能。通常,定义一个 app 或 Framework 的内部结构时,会使用internal权限。 115 | - fileprivate 访问权限,限制实体仅能在其定义的源文件内访问。当某个功能仅用于某个源文件时,我们就可以通过添加 fileprivate 来隐藏其具体的实现细节。 116 | - private 访问权限定义的实体,仅能在其所定义的作用域内访问。当某个功能仅用于某个声明时,我们就可以通过 private 访问权限,隐藏其具体的实现细节。 117 | 118 | open是最高访问权限(最少限制),private是最低访问权限(限制最多)。 119 | 120 | open访问权限仅适用于类和类成员,它与public访问权限的区别如下: 121 | 122 | - 定义为 public,或其它更严格访问权限的类,仅能在其所定义的模块内被继承。 123 | - 定义为 public,或其它更严格访问权限的类成员,仅能在其所定义的模块内被override(覆盖、重写)。 124 | - 定义为 open 访问权限的类,既可以在其所定义的模块内被继承,也可以在其它引入其被定义模块的模块中被继承。 125 | - 定义为 open 访问权限的类成员,既可以在其所定义的模块内override(覆盖、重写),也可以在其它引入其被定义模块的模块中override(覆盖、重写)。 126 | (译者:在a模块中用 open 定义了一个A类,那么A类既可以在a模块中被继承,也可以在任何引入了a模块的b,c...等模块中被继承) 127 | 128 | 将一个类标记为 open 访问权限,即表明你已充分考虑到该类会被其它模块当作父类继承,并为此妥善的编写了该类的代码。 129 | 130 | ## 11.@objc & dynamic 131 | 关键字,摘录自王巍 [《Swift tips》](http://swifter.tips/objc-dynamic/) 132 | @objc 当 Objective-C 需要引用 Swift 代码,我们可以将需要暴露给 Objective-C 使用的任何地方 (包括类,属性和方法等) 的声明前面加上 @objc 修饰符。 133 | @objc 修饰符的另一个作用是为 Objective-C 重新声明方法或者变量的名字 134 | @dynamic 当需要用刀某些动态特性时,即可用此关键词,例如KVO。 135 | 136 | 扩展阅读:[《Swift Runtime动态性分析》](http://www.infoq.com/cn/articles/dynamic-analysis-of-runtime-swift)由尹峥伟发表于[InfoQ](http://www.infoq.com/cn/)。该文更好的从运行时介绍了Swift的“动态性”。 137 | 138 | 139 | ## 12.@escaping & @noescape 140 | 摘录自[《What Do @escaping and @noescape Mean In Swift 3》](https://cocoacasts.com/what-do-escaping-and-noescaping-mean-in-swift-3/) 141 | 142 | ## 13.溢出运算符 143 | 当向一个整型常量或变量赋一个超出其所允许范围的数值时,默认情况下,Swift不会生成一个无效的数值,而是报错。该做法为过大或过小数值地操作提供了额外的安全性。 144 | 145 | 例如,Int16 整型能容纳从 -32768 到 32767 的有符号整型。如果尝试向 Int16 有符号整型的常量或变量赋超过其容纳范围的数值,则会报错: 146 | 147 | ``` Swift 148 | var potentialOverflow = Int16.max 149 | // potentialOverflow 被赋值为 Int16 整型所能容纳的最大值 32767。 150 | potentialOverflow += 1 151 | // 该操作会引发错误 152 | ``` 153 | 154 | 在处理过大或过小值时提供相应的错误处理,可以让我们对边界的操作更加灵活。相对于报错,我们可以对数值进行截取。Swift提供了三种溢出运算符来让系统支持整型溢出运算。这些运算符均以 & 开头: 155 | - 溢出加 &+ 156 | - 溢出减 &- 157 | - 溢出乘 &* 158 | 159 | ## 14.特殊的字面量 160 | 161 | 在 C 中调试时我们经常会用到 `__FILE__`,`__FUNCTION__` 等特殊字面量,来获取一些信息,那么在 Swift 中怎么用呢? 162 | 163 | ```Swfit 164 | print(#file)// 打印当前文件 165 | 166 | func hello() { 167 | print(#function)//打印当前函数 168 | } 169 | hello() 170 | 171 | print(#line)//打印当前行号 172 | 173 | print(#column)// 打印所处的列,7 174 | ``` 175 | 176 | 通过上面的例子可以看出,Swift 采用 `#` + 关键字小写的方式,很好的照顾了那些从其它语言转过来的朋友们。 177 | 178 | ## 15.用函数做参数以简化代码 179 | 摘录自:[@南峰子_老驴](http://m.weibo.cn/3321824014/4060979186247385) 180 | 181 | ```Swift 182 | let setInt: (Int, String) -> Void = UserDefaults.standard.set 183 | let getInt: (String) -> Int = UserDefaults.standard.integer 184 | 185 | setInt(10, "Ten") 186 | print(getInt("Ten")) 187 | ``` 188 | 189 | ## 16.包名冲突 190 | 191 | 今天在尝试项目中引入“NetworkExtension”的时候,发现编译器提示“File 'NameOfCrrentFile.Swift is a part of module 'NetworkExtension' ; ignoring import”, 后来在苹果[官方论坛](https://forums.developer.apple.com/thread/45186)上找到了答案,原来是工程名“NetworkExtension”与要引入的包名冲突了,由于**.swift已经是“NetworkExtension”工程(实际也是一个包)一部分,所以Swift拒绝再在该包中引入一个同名的包。 192 | 193 | Swift虽然支持命名空间,但是随即也带来了类似的问题,看来接下来有必要好好了解下Swift的包管理和命名空间问题。 194 | 195 | ## 17. #keyPath语法糖 196 | 摘录自:[@南峰子_老驴](http://m.weibo.cn/3321824014/4060979186247385) 197 | 198 | 在使用KVC或KVO时,经常会犯一些因KeyPath拼写不正确,从而导致应用崩溃的错误。为此,Swift 3中引入了 #keyPath()表达式,不多解释,直接看代码: 199 | 200 | ```Swift 201 | class Person: NSObject { 202 | dynamic var firstName: String 203 | 204 | init(firstName: String) { 205 | self.firstName = firstName 206 | } 207 | } 208 | 209 | let chris = Person(firstName: "Chris") 210 | 211 | let keyPath = #keyPath(Person.firstName) 212 | chris.value(forKey: keyPath) 213 | ``` 214 | ## 18. as、as?、as! 215 | 216 | 从字面理解`as`作为什么的意思,那也在Swift中它的作用就是类型转换(type casting),那么三个as分别有什么不同呢? 217 | 218 | `as` 直接转换,首先,其可用于从`sub class`到`super class`/`Any`的向上转换(upcast),这种转换没有额外的风险,所以直接`as`: 219 | 220 | ``` Swift 221 | class People { 222 | } 223 | 224 | class Child: People { 225 | } 226 | 227 | let john = Child() 228 | let people: People = johon as People 229 | 230 | ``` 231 | 其次,其可用于消除歧义,指明类型; 232 | 233 | ``` Swift 234 | let num1 = 7 as CGFloat 235 | 236 | let num2 = 7 as Int 237 | 238 | let num3 = 3.14 as Int 239 | 240 | let num4 = (7 / 2) as Double 241 | ``` 242 | 243 | 最后,其还可以在 Switch 244 | 245 | ``` Swift 246 | class Boy: Child { 247 | } 248 | 249 | class Girl: Child { 250 | } 251 | 252 | let child: Child = Boy() 253 | 254 | switch child { 255 | case let boy as Boy: 256 | // 257 | break 258 | case let girl as Girl: 259 | // 260 | break 261 | default: 262 | break 263 | } 264 | ``` 265 | 266 | `as?`用于向下转换(downcast)。尝试去转换并返回一个可选类型,成功则包含一个值,失败则为`nil`,所以转换结果需要判断。 267 | 268 | ``` Swift 269 | class People { 270 | } 271 | 272 | class Child: People { 273 | } 274 | 275 | let people: People = People() 276 | 277 | if let child = people as? Child { 278 | print("转换成功") 279 | } else { 280 | print("转换失败") 281 | } 282 | 283 | ``` 284 | 285 | `as!` 强制转换,成功返回可选类型,失败则导致运行时错误。所以在操作前,要求开发者非常清楚待转换对象的类型。 286 | 287 | ``` Swift 288 | let someOne: Any = People() 289 | let people = someOne as! People // 转换成功 290 | 291 | let someOne: Any = "Who are you" 292 | let people = someOne as! People // 转换失败,崩溃。 293 | 294 | ``` 295 | ## 19.闭包的几种表达 296 | 297 | 闭包不是什么新生事物,但 Swift 仿佛对此情有独衷,提高了非常丰富的支持(效仿茴字吗?): 298 | 299 | ``` Swift 300 | let score = [1,3,40,32,2,53,77,13] 301 | 302 | // Version 1 303 | func sortAscending (_ i: Int , _ j: Int) -> Bool { 304 | return i < j 305 | } 306 | let scoreSorted1 = score.sorted(by: sortAscending) 307 | // Version 2 308 | let scoreSorted2 = score.sorted { (i, j) -> Bool in 309 | return i > j 310 | } 311 | // Version 3 312 | let scoreSorted3 = score.sorted {i, j in i < j} 313 | 314 | // Version 4 315 | let scoreSorted4 = score.sorted(by: {$0 < $1}) 316 | 317 | // Version 5 318 | let scoreSorted5 = score.sorted{$0 < $1} 319 | 320 | // 以上五种表达方式是等价的。 321 | ``` 322 | 323 | 注意:不是所有的闭包都可以采用上面的任意一种写法。 324 | 325 | ## 20.@discardableResult 326 | 327 | 摘录自 [Use Your Loaf](https://useyourloaf.com/) 的 [SWift 3 Warning of Unused Result](https://useyourloaf.com/blog/swift-3-warning-of-unused-result/) 328 | 329 | 在 Swift 2.x的时候,带有返回值的方法,如果在调用时没有使用返回值,编译器是不会给任何警告提示的,但是可以通过添加一下关键字来让编译器给予提示: 330 | 331 | ``` Swift 332 | @warn_unused_result func doSomething() -> Bool { 333 | return true 334 | } 335 | ``` 336 | 这是在调用该方法,就可以正确的收到警告信息了——“Result of call to 'doSomething()' is unused”。 337 | 338 | 到了 Swift 3.0 就不需要这么操作了,因为编译器已经可以默认检查。但那问题又来了,如果不想要警告怎么办?那就要用到@discardableResult: 339 | 340 | ``` Swift 341 | @discardableResult func doSomething() -> Bool { 342 | return true 343 | } 344 | ``` 345 | 346 | 如此就不会在收到提示了。 -------------------------------------------------------------------------------- /Interview_questions/Interview_questions_about_net_protocol.md: -------------------------------------------------------------------------------- 1 | # 面试题系列之网络协议 2 | 3 | ## 1. 网络协议基础 4 | 5 | ### 1.1 七层模型 6 | 1. 物理层: 双绞线、无线 2.4G、5G、光线 7 | 2. 数据链路层:以太网 8 | 3. 网络层:ARP、IPv4、IPv6、ICMP、IPsec 9 | 4. 传输层:TCP、UDP、UDP-Lite、SCTP、DCCP 10 | 5. 会话层: 11 | 6. 表示层: 12 | 7. 应用层:HTTP、SSH、FTP、DNS、TELNET、SSL/TLS、SIP 13 | 14 | ### 1.2 ping 是什么协议 15 | ICMP(Internet Control and Message Protocal) 16 | 17 | ### 1.3 传输层和网络层分别是做什么的? 18 | - 网络层,用来解决从哪来,到哪去的问题,即在浩瀚网海之中如何准确传递消息 19 | - 传输层,用来传输数据,在网络层的基础之上进行交流,解决消息如何传递的问题。 20 | - 应用层,解决消息如何封装和读取的问题 21 | 22 | ## 2. IP 协议 23 | 24 | ## 3. 传输层协议 25 | TCP/UDP 的区别,头部对比: 26 | 27 | | TCP | UDP | 28 | |:----|:----| 29 | |面向链接的|无连接| 30 | |可靠的协议|不可靠的| 31 | |效率相对较低|高速、实时性好| 32 | |顺序控制|无| 33 | |重发控制|无| 34 | |流量控制|无| 35 | |拥塞控制|无| 36 | |20个字节,加上可选就是24个字节|固定 8个字节| 37 | ### 3.1 TCP 协议 38 | 39 | #### 3.1.1 TCP的三次握手四次挥手 40 | 41 | MSS Maximum Segments Size 最大消息段长度就是在三次握手的过程中确立的。 42 | 建立连接的三次握手过程: 43 | 44 | ![TCP建立连接](https://ask.qcloudimg.com/http-save/yehe-1446775/uwl5mh9ogf.png?imageView2/2/w/1620) 45 | 46 | 1. 客户端发送的 TCP 报文中标识位 SYN 置1,初始序号 seq=x(随机)。Client 进入SYN_SENT状态,等待 Server 确认; 47 | 2. 服务器收到数据包后,根据标识位 SYN=1 知道 Client 请求建立连接,Server将标识位 SYN 和 ACK 都置为1, ack=x+1,随机产生一个初始序列号 seq=y,并将该数据不傲发送给 Client 以确认建立连接请求,Server 进入 SYN_RCVD 状态; 48 | 3. Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给Server。Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了 49 | 50 | 断开连接的四次回收过程: 51 | 52 | ![TCP断开连接](https://ask.qcloudimg.com/http-save/yehe-1446775/ok7sodrcza.png?imageView2/2/w/1620) 53 | 54 | 1. Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态 55 | 2. Server收到FIN后,发送一个ACK给Client,确认序号为u + 1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态 56 | 3. Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态 57 | 4. Client收到FIN后,Client进入TIME_WAIT状态(主动关闭方才会进入该状态),接着发送一个ACK给Server,确认序号为w + 1,Server进入CLOSED状态,完成四次挥手 58 | 59 | 60 | #### 3.1.2 TCP滑动窗口,窗口大小,如何确定的? 61 | TCP 协议为了达到更高效的数据发送效率, 会使用一个叫滑动窗口的机制来批量发送数据, 其形式如图: 62 | ![TCP滑动窗口](http://user-image.logdown.io/user/12785/blog/12037/post/730737/TsL7H0lRS9W8qRXq1xdS_滑动窗口1) 63 | 64 | 窗口大小是如何确定的呢? 65 | tcp 有个机制叫慢启动. 一般来讲, 如果 MSS 为 1460 Bytes (以太网标准), 则窗口初始大小设置为 3 MSS 大小. 此后, 根据每次数据往返的 ack, 会逐渐增大这个值. 首次开始数据传输时, 拥塞窗口大小会以 1, 2, 4 这样的指数倍增长, 直到发生超时重发, 窗口会重新开始计算, 并设置一个慢启动阈值, 这个阈值为窗口大小的一半, 当窗口大小超过阈值时, 窗口的增长速率按照一个小于 MSS 大小的比例增长。 66 | 67 | 如果遇超时重传的情况, 则拥塞窗口大小会重新设置为 (慢启动阈值 + 3 MSS). 而阈值会重新设置为当前窗口的一般. 窗口变化如图: 68 | 69 | ![TCP拥塞控制](http://user-image.logdown.io/user/12785/blog/12037/post/730737/sqeDKQS8SKUrvlqIxrVn_拥塞窗口) 70 | 71 | 那么, 最终的窗口大小其实是由接收方的窗口控制参数和拥塞窗口大小共同决定的, 实际发送的窗口数据量大小, 是两者中的较小值. 72 | 73 | #### 3.1.4 TCP是如何保证可靠的 74 | 75 | TCP 的每个分断都有自己的序号,接受端收到一个分段后会向发送方确认,并附带下一分段的编号,发送收到确认后,就会知道该编号以前到接受方都已经收到了。如果超时未收到确认,那么发送方将重发。 76 | 77 | ### 3.2 UDP 协议 78 | 79 | ### 3.2.3 UDP可以实现一对多吗?怎么实现? 80 | 81 | ## 4. HTTP 协议 82 | HTTP协议(HyperTextTransferProtocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议 83 | 84 | [引用](https://cloud.tencent.com/developer/article/1351472) 85 | 86 | **无状态协议** 87 | 88 | **怎么解决无状态** 89 | ### 4.1 URI 和 URL 90 | 91 | - URI(Uniform Resource Identifier),统一资源标识符,用来唯一地标识一个资源; 92 | - URL(Uniform Resource Locator),统一资源定位器,它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何定位这个资源。 93 | 94 | #### 4.1.1 URI 95 | 组成部分: 96 | 97 | 1. 访问资源的命名机制 98 | 2. 存放资源的主机名 99 | 3. 资源自身的名称,由路径表示,着重强调于资源 100 | 101 | #### 4.1.2 URL 102 | 组成部分: 103 | 104 | 1. 协议(或称为服务方式) 105 | 2. 存有该资源的主机IP地址(有时也包括端口号) 106 | 3. 主机资源的具体地址。如目录和文件名等 107 | 108 | ### 4.1 HTTP 报文格式 109 | 110 | #### 4.1.1 请求报文 111 | ![请求报文格式](https://img-blog.csdn.net/20170330192653242?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9sbW9meQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 112 | 113 | #### 4.1.2 响应报文 114 | ![响应报文格式](https://img-blog.csdn.net/20170330192754102?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9sbW9meQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 115 | 116 | #### 4.1.3 常用头部 117 | 118 | **通用首部字段(请求报文与响应报文都会使用的首部字段)** 119 | 120 | - Date:创建报文时间 121 | - Connection:连接的管理 122 | - Cache-Control:缓存的控制 123 | - Transfer-Encoding:报文主体的传输编码方式 124 | 125 | **请求首部字段(请求报文会使用的首部字段)** 126 | 127 | - Host:请求资源所在服务器 128 | - Accept:可处理的媒体类型 129 | - Accept-Charset:可接收的字符集 130 | - Accept-Encoding:可接受的内容编码 131 | - Accept-Language:可接受的自然语言 132 | 133 | **响应首部字段(响应报文会使用的首部字段)** 134 | 135 | - Accept-Ranges:可接受的字节范围 136 | - Location:令客户端重新定向到的URI 137 | - Server:HTTP服务器的安装信息 138 | 139 | **实体首部字段(请求报文与响应报文的的实体部分使用的首部字段)** 140 | 141 | - Allow:资源可支持的HTTP方法 142 | - Content-Type:实体主类的类型 143 | - Content-Encoding:实体主体适用的编码方式 144 | - Content-Language:实体主体的自然语言 145 | - Content-Length:实体主体的的字节数 146 | - Content-Range:实体主体的位置范围,一般用于发出部分请求时使用 147 | 148 | ### 4.2 HTTP 缓存控制 149 | 缓存控制头部:Cache-Control,其值详如下: 150 | 151 | - max-stale 缓存可以随意提供过期的文件 152 | - max-stale=\ 增加时间秒,改时间内不能过期 153 | - min-refresh=\ 至少在未来指定的秒数内要保持新鲜。 154 | - max-age=\ 无法返回缓存时间长于 s 秒的文档 155 | - no-cache 除非在验证,否则不接受缓存 156 | - no-store 缓存应该尽快从过年存储中删除文档的所有痕迹 157 | - only-if-cached 只有当缓存中有副本时,才会获取一份副本。 158 | 159 | 缓存新鲜度验证条件头部: 160 | 161 | - If-Modified-Since: 如果从指定日期之后文档被修改过,就执行该方法。可以与 Last-Modified 服务器响应头部配合使用。 162 | - If-None-Match: 如果与标签不同,就执行请求方法。 163 | 164 | 弱验证器,即允许不更新缓存的变化度。 165 | 166 | ### 4.2 HTTP请求有哪些方法?如何选择? 167 | 168 | - GET 用来向服务器发送请求 169 | - POST 用于将数据发送给服务以创建和更新资源 170 | - HEAD 与 GET 类似,但是没有响应题,仅传输状态行和标题部分 171 | - OPTION 用来描述了目标资源的通信选项 172 | - PUT 向服务器插入内容 173 | - DELETE 向服务器删除内容 174 | - CONNECT 用来建立到给定URI标识的服务器的隧道;它通过简单的TCP / IP隧道更改请求连接,通常实使用解码的HTTP代理来进行SSL编码的通信 175 | - TRACE 用于沿着目标资源的路径执行消息环回测试 176 | 177 | ### 4.3 cookie 178 | Cookie是什么? Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。 179 | 180 | 为什么需要Cookie? 因为HTTP协议是无状态的,对于一个浏览器发出的多次请求,WEB服务器无法区分 是不是来源于同一个浏览器。所以,需要额外的数据用于维护会话。 Cookie 正是这样的一段随HTTP请求一起被传递的额外数据。 181 | 182 | Cookie能做什么? Cookie只是一段文本,所以它只能保存字符串。而且浏览器对它有大小限制以及 它会随着每次请求被发送到服务器,所以应该保证它不要太大。 Cookie的内容也是明文保存的,有些浏览器提供界面修改,所以, 不适合保存重要的或者涉及隐私的内容。 183 | 184 | Cookie 的限制。 大多数浏览器支持最大为 4096 字节的 Cookie。由于这限制了 Cookie 的大小,最好用 Cookie 来存储少量数据,或者存储用户 ID 之类的标识符。用户 ID 随后便可用于标识用户,以及从数据库或其他数据源中读取用户信息。 浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;如果试图存储更多 Cookie,则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。 185 | 186 | 通过前面的内容,我们了解到Cookie是用于维持服务端会话状态的,通常由服务端写入,在后续请求中,供服务端读取。 下面本文将按这个过程看看Cookie是如何从服务端写入,最后如何传到服务端以及如何读取的。 187 | 188 | Cookie 可以笼统的分为两类: 189 | 190 | - 会话 cookie 一种临时 cookie,记录用户访问站点是的设置和偏好。推出浏览时要删除 191 | - 持久 cookie 生存时间更长,存储在硬盘上,即使重启也仍然保留。用于维护用户会周期性访问的站点的配置文件或登录名。 192 | 193 | 通过下面两个响应头在客户端设置 cookie 194 | 195 | - Set-Cookie 196 | - Set-Cookie2 197 | 198 | #### cookie 的域属性 199 | Cookie 通常是谁创建,才会发送给谁。产生 Cookie 的服务器可以向 Set-Cookie 响应头添加一个 Domain 属性来控制哪些站点可以看到哪个 cookie。例如: 200 | 201 | ```javascript 202 | Set-cookie: user="mary17"; domain="airtravelbargains.com" 203 | ``` 204 | 205 | #### cookie 路径属性 206 | 目的与域类似,只是更细化: 207 | 208 | ```javascript 209 | Set-cookie: user="mary17"; domain="airtravelbargains.com"; path=/auto 210 | ``` 211 | 212 | 213 | ### 网关中的 HTTP 隧道 214 | 215 | HTTP/1.1 规范保留了 CONNECT 方法,但没有对其功能进行描述 216 | 217 | CONNECT 方法请求隧道网管创建一条到达任意目的服务器和端口的 TCP 连接(隧道),并对客户端和服务器之间的后续数据进行忙转发。 218 | 219 | 一旦 TCP 连接建立,网关会发送一条 HTTP 200 Connction Established 响应来通知客户端。注意该响应与普通的响应不同,不需要包含 Content-Type 头部。因此此连接针对的是字节流,不是涉及内容。 220 | 221 | 至此,隧道建立完毕。 222 | 223 | 在隧道建立之前,可以要求客户端提供认证信息,通过后再予以建立。 224 | 225 | ### 中继 226 | HTTP 中继(relay)是没有完全遵循 HTTP 规范的简单 HTTP 代理。中继负责处理 HTTP 中建立连接的部分,然后对字节进行盲转发。 227 | 228 | ### 客户端识别 229 | 230 | 头部 231 | |头部名称|头部类型|描述| 232 | |:------|:------|:--| 233 | |From|请求|用户的 E-mail 地址| 234 | |User-Agent|请求|用户的浏览器软件| 235 | |Referer|请求|用户是从这个页面上依照连接跳转过来的| 236 | |Authorization|请求|用户名和密码| 237 | |Client-IP|请求|扩展(请求)|客户端的 IP 地址| 238 | |X-Forwarded-For|扩展(请求)|客户端的 IP 地址| 239 | |Cookie|扩展(请求)|服务器产生的 ID 标签| 240 | 241 | ### HTTP 认证机制 242 | 243 | #### 基本认证 244 | 1. 客户端正常发送请求; 245 | 2. 服务端进行质询,头部带上 WWW-Authenticate,服务器会头部的值中知名需哟啊鉴权的区域,并返回 401 状态码 Unauthorized 246 | 3. 客户端收到响应后,进行授权,头部带上 Authorization:服务指定的密钥 247 | 4. 服务端响应 200 OK,并在头部带上 Authentication-Info 248 | 249 | #### 安全域 250 | 安全域(security realm),用于指定可访问的范围。 251 | 252 | #### 摘要认证 253 | 基本认证存在明文传输的问题,摘要认证正式要解决该问题。摘要认证的安全程度只是略高于基本认证,更安全的是HTTPS 254 | 255 | 通常是 摘要+盐 256 | 257 | 258 | ## 5 HTTPS 协议 259 | [HTTPS加密协议详解](https://www.wosign.com/faq/faq2016-0309-01.htm) 260 | 261 | ### 5.1 HTTPS 原理 262 | HTTPS 在真正开始 HTTP 请求之前,先与服务器进行几次握手验证,以确定双方身份,如图: 263 | 264 | ![HTTPS SSL 建立流程](https://www.wosign.com/News/images/20160309041.png) 265 | 266 | 验证流程: 267 | 268 | (1).client_hello 269 | 270 | 客户端发起请求,以明文传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数,扩展字段等信息,相关信息如下: 271 | 272 | 支持的最高TSL协议版本version,从低到高依次 SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2,当前基本不再使用低于 TLSv1 的版本; 273 | 274 | 客户端支持的加密套件 cipher suites 列表, 每个加密套件对应前面 TLS 原理中的四个功能的组合:认证算法 Au (身份验证)、密钥交换算法 KeyExchange(密钥协商)、对称加密算法 Enc (信息加密)和信息摘要 Mac(完整性校验); 275 | 276 | 支持的压缩算法 compression methods 列表,用于后续的信息压缩传输; 277 | 278 | 随机数 random_C,用于后续的密钥的生成; 279 | 280 | 扩展字段 extensions,支持协议与算法的相关参数以及其它辅助信息等,常见的 SNI 就属于扩展字段,后续单独讨论该字段作用。 281 | 282 | (2).server_hello+server_certificate+sever_hello_done 283 | 284 | (a) server_hello, 服务端返回协商的信息结果,包括选择使用的协议版本 version,选择的加密套件 cipher suite,选择的压缩算法 compression method、随机数 random_S 等,其中随机数用于后续的密钥协商; 285 | 286 | (b)server_certificates, 服务器端配置对应的证书链,用于身份验证与密钥交换; 287 | 288 | (c) server_hello_done,通知客户端 server_hello 信息发送结束; 289 | 290 | (3).证书校验 291 | 292 | 客户端验证证书的合法性,如果验证通过才会进行后续通信,否则根据错误情况不同做出提示和操作,合法性验证包括如下: 293 | 294 | 证书链的可信性 trusted certificate path,方法如前文所述; 295 | 296 | 证书是否吊销 revocation,有两类方式离线 CRL 与在线 OCSP,不同的客户端行为会不同; 297 | 298 | 有效期 expiry date,证书是否在有效时间范围; 299 | 300 | 域名 domain,核查证书域名是否与当前的访问域名匹配,匹配规则后续分析; 301 | 302 | (4).client_key_exchange+change_cipher_spec+encrypted_handshake_message 303 | 304 | (a) client_key_exchange,合法性验证通过之后,客户端计算产生随机数字 Pre-master,并用证书公钥加密,发送给服务器; 305 | 306 | (b) 此时客户端已经获取全部的计算协商密钥需要的信息:两个明文随机数 random_C 和 random_S 与自己计算产生的 Pre-master,计算得到协商密钥; 307 | 308 | enc_key=Fuc(random_C, random_S, Pre-Master) 309 | 310 | (c) change_cipher_spec,客户端通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信; 311 | 312 | (d) encrypted_handshake_message,结合之前所有通信参数的 hash 值与其它相关信息生成一段数据,采用协商密钥 session secret 与算法进行加密,然后发送给服务器用于数据与握手验证; 313 | 314 | (5).change_cipher_spec+encrypted_handshake_message 315 | 316 | (a) 服务器用私钥解密加密的 Pre-master 数据,基于之前交换的两个明文随机数 random_C 和 random_S,计算得到协商密钥:enc_key=Fuc(random_C, random_S, Pre-Master); 317 | 318 | (b) 计算之前所有接收信息的 hash 值,然后解密客户端发送的 encrypted_handshake_message,验证数据和密钥正确性; 319 | 320 | (c) change_cipher_spec, 验证通过之后,服务器同样发送 change_cipher_spec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信; 321 | 322 | (d) encrypted_handshake_message, 服务器也结合所有当前的通信参数信息生成一段数据并采用协商密钥 session secret 与算法加密并发送到客户端; 323 | 324 | (6).握手结束 325 | 326 | 客户端计算所有接收信息的 hash 值,并采用协商密钥解密 encrypted_handshake_message,验证服务器发送的数据和密钥,验证通过则握手完成; 327 | 328 | (7).加密通信 329 | 330 | 开始使用协商密钥与算法进行加密通信。 331 | 332 | 注意: 333 | 334 | (a) 服务器也可以要求验证客户端,即双向认证,可以在过程2要发送 client_certificate_request 信息,客户端在过程4中先发送 client_certificate与certificate_verify_message 信息,证书的验证方式基本相同,certificate_verify_message 是采用client的私钥加密的一段基于已经协商的通信信息得到数据,服务器可以采用对应的公钥解密并验证; 335 | 336 | (b) 根据使用的密钥交换算法的不同,如 ECC 等,协商细节略有不同,总体相似; 337 | 338 | (c) sever key exchange 的作用是 server certificate 没有携带足够的信息时,发送给客户端以计算 pre-master,如基于 DH 的证书,公钥不被证书中包含,需要单独发送; 339 | 340 | (d) change cipher spec 实际可用于通知对端改版当前使用的加密通信方式,当前没有深入解析; 341 | 342 | (e) alter message 用于指明在握手或通信过程中的状态改变或错误信息,一般告警信息触发条件是连接关闭,收到不合法的信息,信息解密失败,用户取消操作等,收到告警信息之后,通信会被断开或者由接收方决定是否断开连接 343 | 344 | ### 5.2 HTTPS 用到的主要加密算法 345 | 非对称加密算法:RSA,DSA/DSS 在客户端与服务端相互验证的过程中用的是对称加密 346 | 对称加密算法:AES,RC4,3DES 客户端与服务端相互验证通过后,以随机数作为密钥时,就是对称加密 347 | HASH算法:MD5,SHA1,SHA256 在确认握手消息没有被篡改时 348 | 349 | ### 5.3 HTTPS 密钥协商交还的过程,中间人攻击,即charles抓包的原理 350 | 熟悉了上面双方互下确认的过程,在中间截获,并用自己的证书进行替换转发即可。 351 | 352 | HTTPS 单向校验原理: 353 | ![HTTPS 单向校验](https://img-blog.csdn.net/20160310160503593) 354 | 355 | HTTPS 双向校验原理: 356 | 357 | ![HTTPS 双向校验](https://img-blog.csdn.net/20160310160503593) 358 | 359 | ## 6. 项目经验 360 | ### 6.1 网络造成卡顿的原因 361 | 362 | 拥塞,TCP拥塞控制 363 | 364 | ### 6.2 断点续传怎么实现?需要设置什么? 365 | 断点续传其实正如字面意思,就是在下载的断开点继续开始传输,不用再从头开始。所以理解断点续传的核心后,发现其实和很简单,关键就在于对传输中断点的把握,我就自己的理解画了一个简单的示意图: 366 | 367 | ![文件断点续传方案](https://img-blog.csdn.net/20141206215834127?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb3dlbjI1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 368 | 369 | 断点续传的关键是断点,所以在制定传输协议的时候要设计好,如上图,我自定义了一个交互协议,每次下载请求都会带上下载的起始点,这样就可以支持从断点下载了,其实HTTP里的断点续传也是这个原理,在HTTP的头里有个可选的字段RANGE,表示下载的范围。 370 | 371 | ### 6.3 在杭州 HTTP 请求服务器响应快,可能离服务器距离近,而在深圳访问就很慢很慢,会是什么原因?如果用户投诉,怎么分析这个问题? 372 | 其实这是一个比较开放的问题,重点在于考察如何排查网络状况: 373 | 374 | 1. 首先排查是否是本地网络的问题,例如访问其它服务器是否够快,或者测试一下本地的网络; 375 | 2. 测试访问网站是否正常,测试DNS解析是否正确,速率如何,丢包是否严重。 376 | 3. 检查 CDN 的状态, 377 | 378 | 379 | 380 | --------------------------------------------------------------------------------