0%

Notion的优缺点

使用Notion一周了,之前在广播里说了很多Notion的特性和优点,但是缺点依然还是有的,这里就把一周使用体验到的优缺点一起说说。

缺点:

  1. 没有离线功能,存在数据安全风险。这是我目前用Notion最大的一个担忧,万一哪天被墙了,梯子又被撤了,或者公司遭到攻击,数据丢失了,或者公司倒闭跑路了,都有可能导致我的数据丢失。

    而且Notion也没有很好的导出功能,只能把单个页面导出成pdf、markdown、csv、html等格式,导出之后就丢失了原来数据之间的关系,以及其他一些Notion特有特性,不能再还原回来。

    等我数据多了后,我需要定期做备份。

  2. 网络有时不稳定,页面有时打开慢,一些数据库页面,打开经常要加载一两秒。使用模版创建新的数据项时,每次都需要加载一会,才能把模版预设的内容加载进来。

    这点在手机上体验非常不好,在手机上点击模版创建新数据,首先会弹出键盘,输入标题,单你输入了一会后,它才会加载好模版,然后它就把你刚才输入的内容清空了,并让键盘消失,你需要重新点击输入框,重新输入。

  3. 不能同时打开多个页面。我之前在印象笔记里的时候,就经常会需要同时打开多个笔记,但在Notion里不行,现在我的解决方法是在浏览器和客户端里同时打开Notion。

优点:

  1. 强大的数据库功能。数据库里可以设置各种格式的数据,可以关联其他数据库,有formula可以自动做一些计算,支持各种自定义的条件筛选和排序,还可以自定义各种不同的视图。得益于此,用户可以建立各种自己想要的数据库和视图,创建专属于自己的记录系统。
  2. 真正的All-in-one。借助数据库之间的关联,各种不同的内容可以互相关联起来。各种内容不再是孤立地放在一个地方,而是能够有效连接。我现在把todo list、记账、日记、读书都放到了Notion里,所有的数据库都可以关联todo list,在一个todo 数据库里管理所有的todo,记账、todo、读书又可以关联到日记,这样在我的日记里能呈现我今天完成的todo、消费情况、读书记录。
  3. 丰富的内容样式,Notion的内容是由一个个block拼装的,每个block都可以选择不同的样式,还可以内嵌其他的网页应用。
  4. 无广告,免费的也能满足个人需求。免费的personal和5美元美元的personal pro几乎没有啥区别。pro增加的4个特性,分别是可以上传大于5M的文件,邀请超过5个的访客,30天的笔记历史,和优先的客服支持。

前言

在一周前我还在使用某象笔记,但最近该笔记丧心病狂地弹框推广告,即使是多年付费老用户也是,于是决定弃用某象笔记,最后经过一番对比,选择了Notion。

实际使用一周后,发现得益于其强大的Database功能,Notion真的能做到官方宣传的All-in-one。它不仅仅是把笔记本、代办事项、项目管理这些功能简单地塞进一个应用里,而且通过Database,能把所有的内容有效地链接起来。

这一周的时间里,我已经在Notion里初步建立了自己的记账、日记、读书笔记的系统。这里我将分享我的记账系统,希望我的分享能让大家领略到Notion Database功能的灵活和强大,并能从自己的实际需求出发,打造属于自己的各种记录系统。

我的记账需求

要创建专属于自己的系统,首先需要分析自己的需求是怎样的,我的记账需求很简单,就是希望能记录我的每项支出,然后能很方便的查看我今日的支出,本月的支出,本年的支出,最近7天的支出,最近一个月的支出等,设置每月的预算,跟踪预算消耗情况,以及不同分类的支出情况。

根据我的需求,我打造出来的记账的首页是长这样的:
471D79BB-219D-4BDC-927E-7F2B4F4B1DBF_1_105

整个页面分三块,最上面是我本月预算、支出的跟踪,让我能随时了解我的支出是否在预算内。左下部分是今日支出和过去7天的支出,右下部分则是过去一个月的支出。表格的底部分别显示各自的支出总额。此外,在子页面,还有年度支出的视图。

S1.建立账单数据库

上图的所有视图分了两个数据库,本月视图是一个单独的年月数据库,其他视图都是同一个账本数据库,这个数据库的数据又会链接到年月数据库。那么我们就先来创建账本和年月的数据库,如下:
2020-12-19_16.18.47

然后,需要把账本和年月链接起来。具体做法是在账本里创建一个 Relation Propery,然后选择年月Databse,然后把账单里的数据,设置到具体的年月,这样账单和年月就建立和双向链接,可以在年月里统计关联到该年月的账单数据。
2020-12-19_16.22.18
2020-12-19_16.27.05

然后,我们在年月里新建一个总支出的Rollup的Property,并把它设置为计算账本支出的总和,这样,我们就可以很方便的拿到某月的总支出数据了。
2020-12-19_16.28.07
2020-12-19_16.28.31

获得了当月的总支出后,我们就可以再设置一个预算,然后和总支出比较,计算预算的消耗情况。这里需要用到Notion Database里的formula功能,需要自己写一点简单的代码。具体的方法,大家可以看这篇文章: https://sspai.com/post/56777,这里我直接贴出代码来:

1
slice("■■■■■■■■■■", (prop("预算") - prop("总支出")) / prop("预算") * 10) + " " + slice(format(prop("总支出") / prop("预算") * 100), 0, 3) + "%"

效果如图:
2020-12-19_17.34.37

S2.搭建不同的视图

至此数据库的基本功能建好了。现在这两个数据库,还都是展示所有的数据,接下来,我们需要在记账首页建立我们本月、今日、过去7天、过去一个月的视图。这需要用到 linked database 和 dabase 的filter。

在记账首页空白出,敲出 /Create linked database ,选择刚才创建的年月Database,就建立了一个新的年月Database 视图,这里还是展示的所有的数据,我们只想要查看当月的数据,就选择filter,筛选name为本月的数据,然后我们把一些不想看的proerty隐藏,只显示我们想看的数据,就完成了。

2020-12-19_16.47.04

接下来,展示今日、过去7天、过去一个月的账单,也是同样的方法,创建账单的linked database,添加filter,分别添加Create Time is today、is within The past week、is within The past month。
2020-12-19_16.51.48

S3.在iPhone上建立捷径,并添加到桌面

最后,为了方便记账,我们可以在iPhone上创建一个捷径。在iPhone上打开Notion的记账页面,点击页面右上角…,然后点击copy link,复制当前页面的url链接。

然后打开快捷指令app,添加新的捷径,添加一个open url的指令,把刚才复制的页面url填入,点击下一步,设置自己喜欢的icon和名称,最后将这个捷径添加到主屏幕就可以了。添加到主屏幕的时候,还可以设置自定义的icon。
IMB_zRgEKH

中国古代政治的一大发展趋势就是皇帝越来越集权专制,而与此相对应的另一大趋势则是,政权越来越开放、越来越平民化,这具体表现在中国古代的科举制度上。

中国传统政治理论,一向重责任,不重主权。在理论上主要的不是政府的主权属于谁,而是政府应该负何种责任。这种重责任,轻主权的政治思想,使中国自秦汉以来,就在选举官员上采用孔子所谓的“选贤与能”的标准,认为只有贤能的人才参与主导政治,才能切实负起理想上政府的重大职能。而要尽可能做到选贤与能,则一方面需要有尽可能多的人才参加选举,这样才能选出最贤能的人,因此中国古代对人才的选举一直以来都没有财产、门第、血统等限制;另一方面则是选举需要有一定的制度和标准,这个制度从汉代的举孝廉,到魏晋的九品中正制,再到唐朝及以后的科举考试制度。其整个选举制度的发展,包括唐以后科举制度的发展都是朝着越来越客观、越来越僵化的方向发展。

汉代最主要的选举制度是举孝廉,即举荐孝子廉吏,自汉武帝以后,汉代地方每年都会定期向中央举孝廉,这些孝廉都是先到郎署先当几年皇帝的侍卫,等到政府需要人的时候再从里面选拨任用。同时汉代设有太学,里面的学生考试毕业分两等,当时称科。甲科出身为郎,不同通过举孝廉就直接任郎官,但这毕竟还只是少数;乙科出身为吏。需要这回到本乡地方政府充当吏职,也就是地方长官的下属。吏在地方干出成绩之后,就可以由地方长官举孝廉到中央,然后经过中央的考试,就可以和郎官一样正式入仕。这样一个青年经过教育、行政实习、举孝廉和考试这四项程序之后就正式进入仕途。

到了汉末战乱时期,地方与中央失却联系,汉代地方向中央举孝廉的制度无法正常实行,于是时任曹操吏部尚书的陈群就制定了“九品中正制”代替举孝廉的制度。所谓的九品中正制,就是先由中央政府的官员中有德望者,分区任命一中正。中正的职责就是将他所知的本乡人才登列簿册,册分九等:上上、上中、上下、中上、中中、中下、下上、下中、下下,不论已仕未仕者,都可以入列,然后送吏部凭册任用人才。其将人分品的标准不是通过政绩,也不是通过考试,而是通过它的社会名誉。

但九品中正制只是战乱时期的暂时制度,因此到了晋代,它就出了很多问题,唐代就把它抛弃了,开始直接以考试来选拨人才。唐代的考试分为两步,先是由礼部主考,通过后及为进士及第。进士及第便有了做官资格了。至于实际分发任用,则一般需要再作幕府幕僚,之后再经吏部考试,才正式入仕。科举制度由于其开放性,除了商人和工人,其他任何人都可以报考;公平性,考试相对长官的察举更客观公正,于是就在唐朝正式确立,并一直沿用到清末。

科举制从宋代开始,日趋走向严密,比如唐代还有公卷通榜之制。可以凭考生的平日诗文定成绩,到了宋代就取消了这一制度,一考定成绩,只要考试及第就可以入仕为官,不需前代规定的实习历练,而且还加上了糊名法,防止请托舞弊。虽然科举制度日趋严密主要是为了防止徇私舞弊,保证选拨的客观公正,但是日趋严密的科举制度却逐渐走向了一种极端,束缚了人才的发展。特别是到了明清,在继承宋代科举制度的基础,又只重考经义,而且只以朱子一家之言为准,于是就演变出了八股文。

虽然科举制度流弊不断,但自唐代以来就一直沿用到清末,成为朝廷选拨人才的唯一方式,甚至现在的公务员也是在延续科举制度。这就表明,科举制自有其独特的作用和价值。这主要体现在三个方面:

一、科举制比较客观公正,有利于选拨人才。

二、科举制的开放平等,有利于平民参与政权,消融社会会阶级。

三、自汉代以来的选举考试制度,一直都是采取分区定额制度,使全国各地优秀人才,永远得平均参加政府,这就有利于促进政治统一。

模仿和重复是唯一的方法

  1. 花更多的时间来学习使用频率更高的英语。
  2. 学习正确的英语,除非必要,尽量少接触错误的英语表达,以免潜移默化。
  3. 不要给自己的英语词汇量设限。

一、单词

单词是学习英语的基础,大部分英语母语者的词汇量在2万-3万5之间,需要把2万词汇量作为目标。每天背单词形成习惯。

背单词的标准,至少是看到单词就能想起它的中文意思。

每天最好背300个单词,一天后基本会忘掉70%。一开始可以先从100个开始,逐渐增加到300个。我现在用扇贝单词每天背105个(35个新词+70旧词)需要半小时,如果增加到300,就需要1个半小时。太多了点,最好还是控制在一小时,一小时的时间尽量多背。

关键是要坚持,即使今天没有完成背单词的任务,但只要背了单词,也是好的,重要的是坚持。

二、语法

母语者不需要专门学习语法,是因为在日常的模仿和重复中,不知觉掌握了规律。但对于外语学习者来说,没有这样的环境和大量的模仿与重复,专门学习语法是有必要的。

在阅读时,如果你遇到了单词都认识,但就是读不懂的句子,那么就说明你在语法方面出现了问题。 这时,请你查阅语法书,通过语法书的解释彻底搞定这种语法现象。

在理解了这个句子之后,把这种语法现象和你自己碰到的句子记下来,当作是你要背的一个生单词,加到你每天的背单词任务里。

语法书:《英语阅读参考手册》、《柯林斯英语语法大全》

三、阅读

养成阅读的习惯,对于雅思阅读成绩低于5.5分的,先从《新概念英语》第三册和第四册的文章开始。

辅助精读法:

你读每一篇文章,都应该让自己对这篇文章的理解达到母语的水平:从第一篇训练文章开始,我们要让每一篇英文文章读起来都像读汉语一样。

至少要把一篇文章读七遍

第一遍:掌握文章大意,标注出所有不认识的单词(不要写在单词边上)
第二、三遍:不看单词意思,能记住认出所有的单词
第四遍: 解决了单词问题,如果还有不懂的句子,就要对不懂的句子做句法分析
第五遍: 做过句子分析后,仍然有不懂的,查看语法书《英语阅读参考手册》或语法书,彻底弄懂相关语法
第六、七遍: 再通读,把单词和句子读通顺,做到像读汉语一样。

精读完成后,把单词记录到背单词软件里,把语法点也记录下来,以后复习记忆。

我们每个人都想要养成一个好习惯,都听过21天养成一个习惯的说法。我本人也尝试过多次去养成各种习惯,比如跑步、写日记、背单词等等,不过毫无意外地大部分都是坚持了一段时间后,就慢慢地不再坚持了,最后放弃了。最长的有坚持了几个月的,最后还是中断放弃了。

以前没有去深思为啥最后就放弃了,直到看到《掌控习惯》的这一章,有种恍然大悟的感觉。以前我都是以某个具体的目标去做这些事,一旦目标达成了,或者感觉目标难以达成,最后就放弃了。

比如我曾经在14年坚持跑步大半年的时间,当时设定了一个跑马拉松的目标,最后当年跑了3次马拉松,跑了马拉松后,就慢慢地很少再跑步了。因为我跑马拉松的目标已经实现了,就一下子没有了继续去跑步的动力了。

只专注于目标,不管最后能不能实现目标,都容易导致我们放弃这个习惯:

1、如果实现了目标,那这也只是一个短暂的改变。比如我上面的跑马拉松,我幸苦训练了半年,跑完了马拉松,这就只是改变了半年。之后又是一切照旧回到原点。而我们要养成习惯的最大的目的,应该是通过习惯每天收获一点,为未来五年、十年、二十年,乃至一生带来巨大收益,而不是为了这一个短暂的目标。

2、如果目标不能实现,或者需要特别长的时间,而我们又只专注于目标,我们就很会感动气馁、挫败,严重地可能会自我怀疑否定,这就很容易使我们放弃坚持这个习惯。

体系和目标则不同,目标侧重结果,体系注重过程。还是以跑步为例,我的目标可能是四小时跑完马拉松,而体系则是每天、每周、每月的跑量,科学地锻炼身体各个部分的素质的方法。这样我只专注于每天具体的跑步训练,只要我这个跑步训练体系正常运行,我就能享受整个过程。体系相比习惯,更加容易坚持,更加持久,收益也更加大。

所以,如果你想要真正获得习惯,就别再设定目标,应该专注于你的体系。

笔者是11号在官网下单的16G+512G的MacBook Pro,25号从上海发货,27号下午到手的。到今天已经差不多用了两天,体验下来,对于iOS开发,总结一句话,就是性能强劲,但尚有兼容问题。

笔者之前用的是18款的6核i7的15.6寸MacBook Pro,依然是16G+521G的组合。首先是用XcodeBanchmark这个项目测试了下,在i7的Mac上花了287秒,M1的Mac花了128秒。

接下来是在真机上编译公司的项目,公司的项目依赖了47个库,先clean cache后在编译,i7的Mac花了270秒,M1的Mac花了165秒。编译速度差不多是i7的1.7倍,提升还是很大的。
截屏2020-11-29 下午3.13.07

另外,在编译过程中,风扇几乎没有转动,所以也没有什么噪音。只是在后面的持续测试中,如果短时间连续编译多次,温度升高后,风扇才开始转动起来。但相比i7的,只要一开始编译,风扇就开始呼呼转动,已经是非常好了。

那么,测试到这里,是不是意味着M1的MacBook Pro对于iOS开发者已经非常完美了呢,并不是。上面测试公司项目的时候,我是直接把旧电脑里的项目整体拷贝到新电脑里测试的。接下来,我在M1的MacBook Pro上配置完整的开发环境时,就遇到了兼容性问题。

第一个兼容问题,是在bundle install 和 pod install 的时候,安装使用ffi时,一直报错,导致pod instal一直失败,最后通过搜索,参照https://github.com/CocoaPods/CocoaPods/issues/10220着里面的方法,用 arch -x86_64 gem install ffi,才最终成功。

第二个兼容问题,是在模拟器中编译时,一直报错”building for iOS Simulator, but linking in object file built for iOS, file ‘…/xxx.a’ for architecture arm64”,试了网上各种方式,都解决不了,依然报错,最后是通过使用Rosetta运行Xcode,才最终在模拟器上编译成功,但由此带来了巨大的性能下降,编译速度直接降到i7的水平。

我自己分析在模拟器中编译失败的原因是,一些第三方的静态库,对于模拟器只编译了x86的版本,没有arm64的版本,而在M1芯片的MacBook里,模拟器也变成了arm架构,需要arm64的版本,因此导致编译到最后链接阶段时报错失败。

因此,我们可以推测,目前有不少第三方库在M1的Mac上会有兼容问题,在GitHub上,我也的确发现了几个这样的库。比如firebase就有这个问题:https://github.com/firebase/firebase-ios-sdk/issues/6520

这个兼容问题,只是在模拟器中编译时才有,如果时在真机编译,是不会有的,因为过去和现在,真机都是arm架构的。

以上是我使用M1的MacBook Pro两天的体验,目前来看着两个兼容问题都不大,都可以解决。

概念

链表(Linked list)是一组数据元素(又称为节点:node)在一个线性序列中的集合,它和数组一样都是一种线性结构,但与数组不同的是,数组内元素的逻辑次序和物理存储地址是对应的,而链表则不是,相邻元素的存储地址未必相邻。为了知道每个元素的地址,上一个元素中会存储下一个元素的地址。因此相比数组访问元素是通过“寻秩访问”(call-by-rank),链表则是“循位置访问”(call-by-position),或者“循链接访问”(call-by-link)。

链表又分为单向链表、双向链表、循环链表,单链表是只有一个前进方向,只能从一个节点链接到下一个节点,而双向链表既可以链接到下一个节点,也可以链接到上一个节点。循环列表则是链表首尾也是链接的。这里只实现单向链表。
IMG_0507

性能

链表的元素读取性能是O(n),在最坏的情况下,需要遍历所有的元素,才能访问到需要的元素。
插入和删除性能是O(1),每次只需要常数的时间就能插入和删除元素。

Swift实现

根据上面的定义,我们可以知道,链表里的元素除了自身的值外,还需要指向下一个元素,所以我们可以定义Node类如下:

1
2
3
4
5
6
7
8
9
public class Node<Value> {
public var value: Value
public var next: Node?
public init(value: Value, next: Node? = nil) {
self.value = value
self.next = next
}
}

同时为了方便打印数据,我们为Node实现CustomStringConvertible协议

1
2
3
4
5
6
7
8
9
extension Node: CustomStringConvertible {
public var description: String {
guard let next = next else {
return "\(value)"
}
return "\(value) -> " + String(describing: next) + " "
}
}

接下来我们实现一个LinkedList类,这个类有head、trail、count三个属性,为了保护数据安全,这三个属性不允许外界直接设置,同样为其实现`CustomStringConvertible``协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class LinkedList<Value> {

public private(set) var head: Node<Value>?
public private(set) var tail: Node<Value>?
public private(set) var count: Int = 0

public init(value: Value? = nil) {
if let value = value {
head = Node(value: value)
tail = head
count = 1
}
}

public var isEmpty: Bool {
return count == 0
}
}

extension LinkedList: CustomStringConvertible {
public var description: String {
guard let head = head else {
return "Empty list"
}
return String(describing: head)
}
}

如此,一个链表就定义好了,但现在只有初始化init方法,还没有查找、插入、删除等操作的方法,接下来我们来一一添加这些方法。

查找:

为了后续操作的方便,我们先添加一个查找方法,通过位置查找对应的节点。上面概念部分已经讲到,链表的存储位置和逻辑秩序是没有对应关系的,要查找某个位置的节点,只能通过前驱节点一步步找下一个节点。因此耗时是O(n),在最坏的情况下需要查找n次。具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public func node(at index: Int) -> Node<Value>? {
guard index < count else {
return 0
}

var currentIndex = 0
var currentNode = head
while currentIndex < index, currentNode != nil {
currentIndex += 1
currentNode = currentNode?.next
}
return currentNode
}

添加操作:

  1. push:添加一个元素到链表头部
  2. append:添加一个元素到链表尾部
  3. insert(after:):添加一个元素到特定节点后

做这三个操作的时候,需要注意要更新head、tail和count。
以上三个添加操作的性能都是O(1)。

IMG_0508

push

添加元素到头部,有两种情况:
一是此时链表还没有头部,还是空链表,那么就需要同时设置head和tail;
二是链表已经有头部了,就需要先把原来的头部作为新节点的下一个节点,然后在把这个节点设为head。

最后count加1。

1
2
3
4
5
6
7
8
public func push(_ value: Value) {
let newNode = Node(value: value, next: head)
head = newNode
if tail == nil {
tail = head
}
count += 1
}

append

添加元素到尾部,也是有两种情况:
一是此时链表为空,没有头部尾部,那么和push操作相同;
二是链表不为空,有尾部,那就需要把新节点设为旧尾部的下一个节点,并把新节点设为tail。
最后count加1.

1
2
3
4
5
6
7
8
9
10
11
12
public func append(_ value: Value) {
if isEmpty {
push(value)
return
}

let newNode = Node(value: value)
tail?.next = newNode
tail = newNode
count += 1
}

insert(after:)

添加元素到某个节点后面,需要先把该节点的next设置为新节点的next,再把新节点设为该节点的next。此时链表不为空,head不需要更新,但是要考虑是否需要更新tail,如果是添加在tail后面,那就和append操作一样,可以直接调用append方法,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
public func insert(_ value: Value, after node: Node<Value>) -> Node<Value> {
if node === tail {
append(value)
return tail!
}

node.next = Node(value: value, next: node.next)
return node.next!
}


insert(at:)

此外还可以添加节点到某个位置index,利用我们上面的查找方法,先找出index-1位置的节点,再调用上面的insert方法。此时就需要注意index == 0的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@discardableResult
public func insert(_ value: Value, at index: Int) -> Node<Value>? {
guard count > 0 else {
push(value)
return head!
}

guard index > 0 else {
insert(value, after: head!)
return head!.next!
}

if let node = node(at: index - 1) {
return insert(value, after: node)
}

return nil
}


删除操作:

删除操作也主要是有三个:

  1. pop:删除头部节点,即head
  2. removeLast:删除尾部节点:即tail
  3. remove(after:):删除某个节点的下一个节点
  4. remove(at:):删除某个位置的节点

删除操作也需要考虑是否需要更新head和tail,同时需要给count做减1操作

以上三个删除操作中1、3的性能是O(1),2的性能是O(n)。

pop

删除头部节点,删除后,需要把head.next设置为新head,实际只需要重新设置head即可。另外需要注意tail是否需要更新,当只有一个节点时,删除一个后,tail也需要设置为nil。

1
2
3
4
5
6
7
8
9
10
11
12
@discardableResult
public func pop() -> Value? {
guard !isEmpty else { return nil }
let oldHead = head
head = oldHead?.next
count -= 1
if tail === oldHead {
tail = nil
}
return oldHead?.value
}

removeLast

删除最后一个节点,其实是要做两件事,一是把tail的前驱节点的next设为nil,再tail设置为前驱节点。因此这里就涉及到要找tail的前驱节点,这个查找操作就需要O(n)的时间。我们上面已经实现了一个查找方法,这里可以直接使用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@discardableResult
public func removeLast() -> Value? {
guard count > 1 else {
return pop()
}

let removedNode = tail
let pre = node(at: count - 2)
pre?.next = nil
tail = pre
count -= 1
return removedNode?.value
}

remove(after:)

删除一个节点的下一个节点,同样需要考虑删除的是否是尾节点,如果是,需要更新tail:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@discardableResult
public func remove(after node: Node<Value>) -> Value? {
defer {
if node.next === tail {
tail = node
}
node.next = node.next?.next
}
if node.next != nil {
count -= 1
}
return node.next?.value
}

remove(at:)

删除特定位置的节点,这个可以利用查找方法,找到前一个位置的node,再调用上面的remove(after:)方法即可,这里需要注意第0个位置时,需要特殊处理:

1
2
3
4
5
6
7
8
9
10
11
12
@discardableResult
public func remove(at index: Int) -> Value? {
guard index >= 0, index < count - 1 else { return nil }
if index == 0 {
return pop()
}
if let node = node(at: index - 1) {
return remove(after: node)
}
return nil
}

12月2日刚入手了2018款的15寸标准顶配MacBook Pro,昨天工作使用了一天,简单谈谈作为iOS开发的使用体验,主要是和我之前在用的2014款13寸MacBook Pro的对比体验。

两款电脑的配置和跑分对比

13寸 MacBook Pro (Mid 2014)
处理器: 2.6GHz 双核 Intel Core i5 处理器 ,Turbo Boost 高达 3.1GHz,3MB L3 cache
内存: 8GB 1600MHz DDR3L

15寸 MacBook Pro (Mid 2018)
处理器: 2.6GHz 六核第八代 Intel Core i7 处理器, Turbo Boost 最高可达 4.3GHz,9MB L3 cache
内存: 16GB 2400MHz DDR4

从配置上来看,2018款相比2014款提升是非常大的,从跑分结果来看也能看出。
2014款跑分单核2832,多核5887;2018款单核4991,多核21190。单核跑分提升76%,多核跑分提升2.6倍。

屏幕快照 2018-12-04 上午7.46.57

实际工作体验:

效率提升
我平时的工作就是iOS开发,从公司项目的build时间来看,2014款完成一次build耗时480秒,2018款耗时220秒,速度提升了一倍,没有跑分上的提升这么大。

除了build时间外,另外一点很大的提升就是代码高亮和自动补齐更快速了。在2014款上,经常会出现代码高亮和自动补齐失效的情况,特别在项目中新建了一个文件后,需要等上很久才能有代码高亮和自动补齐。而在2018款上,新建文件后也能立马有代码高亮和自动补齐,这点对工作效率的提升特别大。

两个缺点
一个是触控板太大了,敲键盘的时候两个手掌都会接触到触控板挺大面积,虽然苹果做了防误触,但一天下来还是出现了多次误触的情况。
具体表现就是,敲着敲着代码,光标突然移到其它位置去了,或者一只手放在键盘上,另一只手操作触控板时,不能移动光标,需要把放在键盘上的手抬起来。

另一个缺点就是感觉散热的确不是特别好,build项目的时候,风扇呼呼转,在触控条顶部区域已经特别热了。

其它
触控条和指纹个人感觉挺鸡肋的,特别是指纹,目前还没有遇到需要指纹的地方,开机解锁都还是要输入密码。而且为了放下触控条,取消了左上角的实体esc键,改为了触控条里的虚拟按键,很不好按。
第三代的蝶式键盘手感也还可以,基本很快就能适应了。
音响效果的确很不错。

#一、读书笔记的原则和方法

1、做笔记的原则:信息一元化原则
就是把关于读书的所有信息都放在一个地方,你的读书愿望单、读书笔记、阅读记录等等都在一个地方,这样方便记录、管理,简单易用更容易坚持。

2、如何做笔记:坚持最重要+笔记五要素固定模板
做读书笔记,首要的是坚持,在完成首要目标的前提下再去想如何做好笔记。而越简单的事情,越容易坚持,因此初期为了坚持,可以记最简单的笔记,比如:
「2017年8月26日,我读了《如何有效阅读一本书》,这本书提供了如何做读书笔记的方法。」

进阶一点,可以使用五要素固定模板:
1)、写笔记的日期
2)、书名
3)、作者
4)、对自己重要的内容(摘抄)
5)、自己的感想

高阶一点的,可以再插入相关信息,比如读到某个人物,可以插入这个人的图片,比如一场战争,可以插入相关地图。

有了固定的模板,进而形成习惯,这样每次记读书笔记就不用去思考应该记录什么内容了。

如果你觉得Kindle不好用,那一定是你没有买Kindle Oasis

从最早的Kindle Touch,到第一代、第二代Kindle PaperWhite,我已经使用过至少三代Kindle,目前使用的Kindle Oasis是我使用的第四代Kindle。在入手Oasis之前,我的旧Kindle PaperWhite已经吃灰很久了,而自从入手Oasis之后,我又爱上了用Kindle阅读,短短入手一周的时间,我已经看完3本书了。