0%

因为团队协作需要,接触到了 Swagger 用以进行 API 设计说明并生成页面。Swagger 的用处并不仅限于此,不过本文仅仅进行对于「API 设计文档」这一主题的总结。

OpenAPI Specification

OpenAPI Specification 是 Linux 基金会的一个项目,试图用一套标准规范的语言描述 RESTful API,提升工程师协作效率,同时使得计算机也能快速理解(解析)。Swagger 使用的就是 OpenAPI 规范。

遵循 OAS 编写 Swagger YAML 文件

格式规范不再复制粘贴了,可参考 Swagger Live Editor 中默认显示的 PetStore 样例,以及 Swagger 官方文档

可以使用 Swagger Live Editor 或者 VS Code 的 Swagger Previewer 插件进行实时编辑和预览。

生成一个可访问的静态页面

通常一个项目会有一个 Dashboard 之类的主页对项目进行一系列说明,包括 API 设计说明,这时就可以把 Swagger 的 yaml 文件生成一个静态页面。找了好几个生成工具如 swagger-to-html 效果都不如人意,十分简陋,都不如 Swagger Editor 的 Preview 好看;但 Swagger 官方却又没提供一个简单的工具。

于是我采用了一个比较简陋的办法,将 Swagger 官方的样例网站 Swagger PetStore 的静态资源下载下来,放入到 Github Pages 的静态目录里去,把默认加载的 yaml 文件 URL 改成自己需要的,即可生成一个效果与 Swagger Editor Preview 相同的静态页面。

注:之后又找到一个工具叫做 Spectacle 似乎效果也不错,可以一试。

事件流

当你点击一组同心圆的圆心,你实际上点击了所有的圆,而不仅仅是最小的圆。
事件流
1)冒泡流 - 由嵌套最底层向上传播
2)捕获流 - 由顶层向下传播(较少使用)

DOM 事件流:DOM 2级事件规定事件流分为三个阶段:事件捕获、处于目标以及事件冒泡阶段。
事件捕获从上向下传播,为事件截获提供了机会,冒泡阶段中元素对事件作出响应。

事件对象

事件被触发时,会产生一个包含该事件相关信息的事件对象。
event.target/currentTarget/this 的关系。

事件处理的内存和性能

事件委托

单独为每个元素添加事件处理程序会占用大量内存(每个函数都是一个对象),应用事件委托,只添加少量事件处理程序,利用冒泡流和 target 属性对不同元素触发执行不同的代码,可以使得内存消耗更低。(比较适合用于键盘、鼠标的事件触发)

移除元素前先移除其链接的事件处理程序

参考文献:
《JavaScript 高级程序设计》第 13 章 事件

引用类型

引用类型是一种数据结构,类似 C++ 的类,对象是引用类型的实例。

Object 是 JavaScript 中最常见的引用类型。

构造 Object 对象的方法有两种:1)new + 构造函数。2)使用对象字面量表示法。

1
2
3
4
var person = {
name: "Jonathan",
age: 20
}

访问对象属性的方法有两种。1)person["name"]。2)person.name。前者的优势是可以利用另一个字符串变量来访问属性。

Array 类型

JavaScript Array 与其他语言相似之处就不谈了。

Array 的动态特性:
1)直接修改 length 改变其长度。
2)直接给 Array 某一位元素赋值即可修改或者添加元素。
3)使用 people[people.length] = "Jonathan" 即可方便的在末尾追加元素。

Array 最多可以包含 4,294,967,295 个元素。

检测 Array 的两种方法:
1)Array.isArray()。
2)instanceof (有缺陷,它假定只有一个全局环境。若包含多个框架,存在不同版本的 Array 构造函数,就会返回错误结果)

Function 类型

JavaScript 中的函数实际上是对象,是 Function 类型的实例。

函数内部有两个特殊对象:arguments 和 this。
1)arguments 包含传入参数的类数组对象。该对象拥有 callee 属性指针,指向拥有这个 arguments 对象的函数。
2)this 引用函数执行的环境对象。

Function 拥有两个属性:length 和 prototype。

使用 call() 和 apply() 函数可以扩展函数运行的作用域,而对象和方法不需要有任何耦合关系。

bind() 函数返回一个绑定作用域的函数版本。objectFunction = generalFunction.bind(object)

单体内置对象:
Global 对象,window 对象,eval 方法(关系到代码注入),Math 对象,暂不详细展开,仅做记录。

参考文献:
《JavaScript 高级程序设计》第五章

JavaScript 是一种弱类型、动态的语言,体现在对象类型不固定,可随时增删属性与方法。

JavaScript 的对象均为引用传递,无按值传递。

函数相关 {
function func() 声明具有声明提升的特性,即函数可在调用之后声明,但是 var func = function 匿名函数声明则不具备。
}

闭包 {
阮一峰博客中的理解是:闭包就是能够读取其他函数内部变量的函数。

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
}

使用 var that = this 方法使得闭包函数可以调用外部函数的 this。

由于 JavaScript 不存在块级作用域,如 for 循环中暂时使用的 i,会在函数内部任何一处有效。为防止潜在的不稳定因素,可以通过匿名函数来模仿块级作用域(私有作用域)。

一些 OO 编程传统特性实现的奇技淫巧 {

私有变量

1
2
3
4
5
6
7
8
9
function MyObject() {
// Private Member
var privateVar = 10;
function privateFunc {}
// Public Method
this.publicMethod = {
return privateFunc;
}
}

上述代码中,除了 PublicMethod 这一途径,没有任何方法访问其私有成员。

静态私有变量

1
2
3
4
5
6
7
8
9
(function() {
// Private Member
var privateVar = 10;
function privareFunc() {}
// Constructor
MyObject = function() {} // Make it global without 'var'
// Public Method
MyObject.prototype.publicMethod = function() {}
})();

单例模式

1
2
3
4
5
6
7
8
9
10
11
12
var singleton = function() {
// Private Member
var privateVar = 10;
function privateFunc() {}
// Public Method
return {
publicVar: true,
publicMethod: function() {
return privateFunc();
}
}
}

确定类型的单例模式
只需要在原型内创建对象,var object = customType(),最后返回 return object 即可。公共接口也可以借由该 object 添加。
}

如何实现一个完善的对象,同时具备私有变量和静态私有变量?

上周踩到的一个坑 {
document.getElementByClassName() 返回的不是一个简单的数组,而是 HTMLCollection 对象。这个集合对象是动态的:当文档改变时,其改变都会影响 HTMLCollection 的内容。

这样一来,想要使某个 class 的所有元素改变到另一个 class,使用如下代码即可:

1
2
3
4
var elements = document.getElementByClassName("old-class");
while (elements.length) {
elements[0].className = "new-class"
}

}

参考文献:
《JavaScript 高级程序设计》 第七章
学习Javascript闭包(Closure) - 阮一峰的网络日志

近年 WWDC 和秋季发布会算是将软件更新与硬件更新完全分离了,让人感觉更清爽更健康了(不知道我在说什么…

看了这么多年发布会,斗胆写点自己的想法。


apple-watch-series-2

Apple Watch Series 2

性能提升以及两倍屏幕亮度

绝对的痛点升级。好在 Series 1(2015 款 Apple Watch 升级)也做了处理器的相应提升。

防水以及内置 GPS

有分量的功能提升,看个人需求值不值比 Series 1 贵的那 700 人民币。

Nike + 版本以及「陶瓷」Edition 版本

哦。


iphone7

iPhone 7 与 iPhone 7 Plus

新增 Jet Black 不锈钢配色

不锈钢似乎确实能提升一定的品质感,但不知 iPhone 大面积用不锈钢效果如何。如果市场表现出色,Apple 也许会第三次 [^1]「引领」个各厂商更新外壳材质。

摄像提升

这个领域毕竟群敌环肆,先有 Sony,后有 Samsung。相机具体水平以及 7 Plus 双摄的可用程度还要看之后 DxO、TheVerge 等媒体的评测。但个人对 Apple 还是很有信心。

具备 Force Touch 特性的 Home 键

大多数人会认为这只是一个微小的升级点,但事实上 Force Touch 的地位非常重要,这涉及到 Apple 产品设计的核心原则。

Force Touch 的核心部件是 Apple 重新设计的线性震动马达 Taptic Engine。当你用手指按压时,Home 键以及 Macbook 上的触摸板实际并未被按压下去,而是利用线性马达的震动反馈,使你感受到按压的反馈。Apple 调教出的这种微妙震动带来的按压反馈完全超越的纯物理按键的按压反馈,这使得当你习惯 Macbook 的 Force Touch 触摸板后,再使用任何笔记本的触摸板甚至旧款 Macbook 的触摸板都会极为不适。iPhone 上的 3D Touch 也是如此。虽然有不少厂商也推出了相应具备压力感应的设备,比如魅族的 3D Press,但其震动反馈依然如一般震动马达相似,与 iPhone 相差甚远。

但 Apple 花费巨大的财力物力只为了一种简单的反馈?没错。

精确的反馈是优秀交互设计的关键之一,这才使得 Force Touch 成为 Apple 一流体验蓝图中重要的一枚棋子。作为普通用户,他们不需要知道 Force Touch 的技术细节,只要能感受到 iPhone 的按键以及 Macbook 的触摸板更易用,这种感受就会成为一种潜意识。这种潜意识,是「很多人其实说不出 iPhone 哪里好,但他们还是认为 iPhone 优秀」的原因之一(以及其他的品牌因素)。

「以用户体验为中心」已经是一句泛滥的口号,然而始终恪守这一点的,也就是 Apple 在内寥寥几家公司而已。

IP67 防水

哦。
不是大多数人的刚需,锦上添花。防水手机现在也不稀奇了也没见几个人整天泡水里玩。

大陆「年年焕新计划」对应美国的「iPhone Upgrade Program」

iphone-upgrade-program-in-china

如果你购买了国行 iPhone 7,同时购买了 AC+,那么就会自动加入这个「年年焕新计划」,即在规定时限内购买新款 iPhone 可以有高额抵扣。详情如上图。

以 iPhone 6S 128G 为例,国行 6188,AC+ 988,总价 7176,若折抵只包含设备价格,即折抵 3094,假设新款价格不变,则每年还需 3094 换新。即第一年 7176 元购买手机,每年花 3094 元购买同档位 iPhone,并且应该会一直附送 AC+ 服务(参照美国「iPhone Upgrade Program」)。

但这里有两个疑问,等 iPhone 7 正式上市可以问问 Apple Care 客服:

  1. 如果港行设备在大陆境内购买 AC+,是否可以参与该计划?
  2. 参与该计划购买的新机是否自动延续 AC+ 的服务期限,且期限是否重新计算?

假设购机预算比较充裕,这个年年焕新计划一定是最好的选择。相比购买港行,小心翼翼地使用,在花心思在二手平台上卖掉,「年年焕新计划」和 AC+ 会更加轻松自由并且节约大量时间。(除非你觉得你的时间不值钱)


airpods

AirPods 与 iPhone 取消的 3.5mm 耳机接口

这是从 Flash 以后,Apple 再一次强制用户更新体验。并且和 Force Touch 一样,又是一个用户体验相关的重要决策。

体验大局

无线耳机毋庸置疑是一个大幅的体验提升。只需第一次蓝牙配对,之后随手拿起耳机就听,不会受到耳机线牵制,完全是个 HDD 到 SSD 的体验跃进。

那些死磕有线耳机又不烧耳机的吐槽 AirPods 和 Apple 取消耳机接口的人都是愚蠢。

3.5mm 这种接口在 Thunder 以及 USB type-C 这个时代早晚都是被淘汰的命。

耳机本身

个人用无线耳机的经验几乎跟自己几年前玩手机的经验差不多(当年买二手卖二手用了两年玩了 30 多部手机),从入耳式如 Jabra Rox 起步,到 Sony AS800BT 等等,蓝牙 MP3 类型的 Sony WS615,以及通勤的 Jawbone Era,头戴还有 Beats Studio Wireless,Parrot Zik 2 等等,到现在的 Bose Soundlink。应该说市面上不错的蓝牙耳机自己都玩了个七七八八,因为本来选择也不多。

(这里实在太懒就随便讲讲)
跟 AirPods 对比的自然是 Jabra Rox 这类入耳式有线蓝牙耳机,
……那么……与 Moto Hint 相似的设计,又做了电池的权衡(多出来的这个怪异的竖杆是电池和无线模块)…….

上面所列举的蓝牙耳机又有两个种类,一种是 Jabra Rox 这样虽然无线(不需要有线连接设备)但又有线(两边耳机的连接线)的,另外一种是 AirPods 这类所 True Wireless 蓝牙耳机(没有手机连接线,两边耳机也没有连接线)。

True Wireless 耳机是我个人十分推崇的,所谓科技的本质就是让人感受不到科技的存在,最初放弃 Sony WS615 就是这个问题,两边庞大的耳机更多会让人觉得怪异,而不是炫酷。True Wireless 这样相对隐藏式的设计,相对自然的外观才是正确的方向。

最开始接触的现在所谓 True Wireless 耳机是 Moto Hint,当时 Hint 的音质以及通话效果还非常的差。AirPods 与 Moto Hint 相似采用 True Wireless 的设计,趋进自然,但又做了电池的权衡(多出来的这个怪异的竖杆是电池和无线模块),显得更加怪异。使得对其评价有些难度,而现在 Hint 不仅有了第二代,Moto VerveOne+ 以及 Samsung IconX 都有发布,我都没体验过,很难再加以评价。

这一方面能写的太多,不过就其他 True Wireless 耳机当前的兼容性、各方面取舍以及媒体评测来看, AirPods 应该是目前技术限制的权衡下均衡性最好的入耳式(?)无线耳机。

Apple 出品

既然是 Apple 出品,AirPods 与其他 Apple 设备配对自然就有相关的体验优化(类比 Apple Smart Battery Case),其地位就更加不可动摇。

[^1]: 第一次是 iPhone 4 的双面玻璃,第二次是 iPhone 5 的全铝合金。我这个说法略有偏颇,至少之前我曾使用过并且非常中意的 HTC One S 已经在 iPhone 5 之前采用了阳极氧化铝的全金属外壳。但确实鉴于 iPhone 行业影响力,事实上此类诸多风潮都是由 iPhone 开始。

【OS X 10.11】

首先,如果你还没有使用 Mackup,你需要知道 Mackup 不适合在多台设备间同时共享配置文件。

这是我最开始的误区。因为有两台设备,想要通过 Mackup 以及 Dropbox 在两台设备之间同步共享配置文件。但这带来的巨大问题是,由于两台设备的用户名不同,导致当配置文件中与用户名相关的内容和路径会集体报错,非常难受,并且有些程序的配置文件被设置软链接之后会出一些莫名其妙的问题,反而会带来更多麻烦。

makefile 简介

make 是一个程序,识别一系列如文件名为 Makefile,makefile 等的文件,根据其中的依赖关系,自动化编译项目。

为什么需要 makefile

对于一个有一定规模的项目,makefile 可以自动化编译,比一个一个手打 gcc/g++ 命令的效率高出许多,并且明确了依赖关系。另一个巨大优势是:使用 makefile 重新编译程序,make 会自动忽略没有修改的文件,只编译被修改过的文件,使得编译效率大大提高。

基本语法

1
2
Target: Dependence1 Dependence2 ...
command ...

Target 即是要编译的目标文件,Dependence 是制作这个目标文件需要的依赖文件,如果这些文件都存在,即条件都满足,就会执行下面 command 中命令。

如:

1
2
3
4
main: main.o
g++ main.o -o main
main.o: main.cpp
g++ main.cpp -o main.o

进阶

多目录

一般来说,自己程序的文件夹中会有 bin, build, src, include 这些目录,分别存放可执行程序,目标文件,源文件,头文件。makefile 一般放在根目录下。你只需在前面加上目录名字即可。如:

1
2
3
4
bin/main: build/main.o
g++ build/main.o -o bin/main
build/main.o: src/main.cpp
g++ -c src/main.cpp -o build/main.o

变量

在很多教程中,会给出声明变量的不同写法。

1
2
var := value
var = value

实际上,前者为简单扩展变量(recursively expanded variable),后者为递归扩展变量(simply expanded variable)。

简单变量会在 被引用时立即赋值 ,比如你这样写:

1
2
3
4
5
6
7
8
a := a
abc := $(a) b c
a := d
abc2 := $(a) b c

test:
echo $(abc)
echo $(abc2)

然后 make test 会发现输出如下:

1
2
3
4
echo a b c
a b c
echo d b c
d b c

而递归变量 只有在被引用的变量在执行时被赋值 ,比如你这样写:

1
2
3
4
5
6
7
a = a
abc = $(a) b c
a = d
abc2 = $(a) b c
test:
echo $(abc)
echo $(abc2)

会发现输出如下,即便 abca = d 之后就被赋值,都只有在 test 执行 abc 的时候,a 才会被替换为它最后的值 d

1
2
3
4
echo d b c
d b c
echo d b c
d b c

自动化变量

makefile 可以使用一些自动化减少劳动量,同样拿之前的例子:

1
2
3
4
bin/main: build/main.o
g++ build/main.o -o bin/main
build/main.o: src/main.cpp
g++ -c src/main.cpp -o build/main.o

可以简化成

1
2
3
4
bin/main: build/main.o
g++ $^ -o $@
build/main.o: src/main.cpp
g++ -c $< -o $@

$^ 可以直接替换成当前目标文件的所有依赖文件;$@ 可以直接替换成当前目标文件的路径名称,而不需要反复输入;$< 会直接替换成当前目标文件的首个依赖文件。

最后

以上只提到了自己比较常用的东西,并未介绍完所有关于 makefile 的知识,比如另外更多的自动化变量等。这些可以自行 google。

推荐一份更加详细深入的教程(只是没有自动化变量):

跟我一起写Makefile

【OS X 10.11.5】

前情提要

不太确定是 2015 年后 Macbook 的因素还是 OS X 10.11 的因素,Boot Camp 安装的方式发生了改变。

根据官网只提供了 2015 年以前的 Boot Camp Assistant 的下载,推测多半是硬件因素。

安装流程

新 Boot Camp 双系统的安装不再需要使用外部 U 盘来创建安装盘,同时也不会自动在成功安装后的 Windows 中自动安装 Boot Camp 助理,使得触摸板双指滑动、右键等等功能均无法使用。

在 2015 年后的 Macbook 中打开 Boot Camp Assistant,选择镜像并选定分区空间后,将会下载 Support Software,并且每次重新安装 Windows 都会重新下载,估计大小为 1 GB 左右。

如果你所在的网络环境下载速度过慢,可以去你所在地的苹果直营店,在直营店 Genius Bar 里接入该店的网线用其内部网络进行下载,速度如下:
boot-camp-download-speed-under-network-in-apple-store

下载安装完毕后,会开始分区,拷贝 Windows 文件。

以上流程完毕会自动重启进入 Windows 安装界面,在安装过程中,需要将划分出的 BOOTCAMP 分区格式化为 NTFS 格式。同时你会发现该分区比你之前分区的大小少了近 8 G,和另一个 OSXRESERVED 分区。

安装完成后进入 Windows 系统,打开 计算机 会发现除了 C 盘意外,还有一个我们之前见过的 OSXRESERVED 盘,点击其目录下的 setup.exe 会重新开始安装 Windows。

进入其目录下的 Boot Camp,再点击 Boot Camp 下的 setup.exe 会进入 Boot Camp 助理的安装引导,完成后,OSXRESERVED 分区会消失,其占用空间会合并进 C 盘。

可能遇见的问题

安装成功并进入 Windows 后,在 计算机 中可能没有 OSXRESERVED 盘,如果多次重启仍然没有,似乎只有尝试重新安装,直至其出现。

我第一次安装时不清楚需要手动安装 Boot Camp 助理,在发现没有 Boot Camp 助理后尝试多次重启,并且在开机时进入启动管理器界面,会看到 OSXRESERVED 分区,但多次重启之后消失,Boot Camp 助理也没有自动安装。似乎新的 Boot Camp 仍然有一些 Bug。

【OS X 10.11.5】

打算用 Boot Camp 装 Win 玩守望先锋,发现空间不够。于是想要把一些大文件拷贝进移动硬盘里。

1)先用 Cmd + C 复制文件,然后再在移动硬盘里使用 Cmd + Opt + V 将文件移动进入移动硬盘。系统中被移动的文件已经消失,但是发现系统可用空间并没有增加。

2)再使用 Cmd + C 复制文件,直接 Cmd + V 粘贴进移动硬盘,然后在本地删除,清空废纸篓。然而系统可用空间并没有增加。

为了查看被删除的文件去了哪里,想要借助 DaisyDisk 来查看。

经过再一次实验后,发现被删除的文件似乎被移动到了另一个地方,在 DaisyDisk 上显示为 (hidden space)[^HiddenSpace],没有权限查看。

daisydisk-hidden-space

[^HiddenSpace]: DaisyDisk 官网对 Hidden Space 的解释

于是…

1)由于我使用了 Time Machine 并且没有插上我用作 Time Machine 备份的硬盘,那么有理由怀疑这是 Time Machine 的本地快照(Local Snapshot)。但是我接上硬盘开启备份后,显示下一个备份只有 27 MB,与我之前删除将近 50 GB 的文件明显不符,于是排除这个可能。

2)去 Genius Bar 进行了一次硬盘检测,据说会做一些非意识性数据的删除,之后查看空间,依然未变。

3)最后尝试重启 Cmd + R 进入恢复模式,用 Disk Utility 对 APPLE SSD 进行 First Aid,再重启,查看空间,依然没变。

但是此时打开 Boot Camp 分区,已经可以分出超过可用空间大小的分区。

- -,所以,这是 Apple 自己的坑吧…

【OS X 10.11.5】

// 此处应有故事起因

然后手快拿 Disk Utility 分了区,分了 75G 出去。

// 此处应有故事过程

然后之间点击 Partition,把饼状图中之前的分区通过点击下方 - 按钮移除,于是出现了以下情况:

Disk-Utility-SSD
Disk-Utility-Partition
分区的确是删除了,APPLE SSD 也依然是 500G 容量但是分区的空间并没有被 Macintosh HD 回收。迷之消失的 75G。

打开终端,输入 diskutil list,回车。

diskutil-list

发现 Recovery HD 变成了 75G,也就是说,移除的分区被 Recovery HD 吃掉了。那么是不是只要把 Recovery HD resize 一下就好呢?然而…

diskutil-resize

并不行。

问过 Apple Genius 之后,得知这个是一个存在很久的 Bug,当你在使用逻辑卷宗(比如使用了 FireVault 加密)时,直接移除分区,Recovery HD 就会直接吞掉分区容量。现在的办法只有把盘格式化掉再重装系统了。

重装完成后,发现虽然 Recovery HD 还在,但无法进入恢复模式。进入后屏幕左上角会提示如上信息。

cannot-enter-recovery-hd

接着就开始自动重启。

再次求助 Apple Genius,Genius 表示也不太清楚是什么问题,做了硬盘测试,没有发现问题。随后再次重装系统测试,此时 Genius 给我讲了一个非官方的「古老」方法解决各种奇奇怪怪的硬盘问题。

当要格式化 APPLE SSD 时,选择 Erase,然后更改 Partition Scheme 为 Apple Partition Map,格式化完成之后再格式化回 GUID Partition 即可。

Genius 表示也不知道为什么… 反正就是有效果,尤其是针对一些老设备。当然对我这台 MacbookPro12,1(2015 Late)似乎也生效了。