原文地址: Logseq 系列之同步插件 | samwei12’s blog
前些天看到群友们讨论 Logseq 要是能够在不同设备间同步插件和主题就好了,目前 Logseq 官方同步已经在小规模测试中,预计再有一两个月就会发布,未来大概率会出同步插件的功能。不过考虑到官方同步的免费使用空间肯定不会很大,如果有大量的本地图片或者其他文档类文件,就不适合免费用了,且主要是现在的 Git 同步方案已经非常好用(重点是免费!),所以还是考虑完善一下现有的方案,短时间内不迁移官方同步。
因为之前在 Logseq 系列之文档管理 | samwei12’s blog 已经分享过个人的文档管理方案,细心的同学可能会发现,我的资源文件中有一个 ebooks 的软链接文件夹,我用它来存放各种电子书,这样的话就可以在不同步电子书到 Git 仓库的同时还能直接使用 Logseq 的本地 PDF 标注功能,这一部分后面有机会可以详细说说。
我这里主要是想提一下系统的文件软链接这个功能,尤其是对于 *nix(包括 Mac)系统来说,软链接几乎可以当作原始文件来使用,由于系统层面的抽象工作做的非常好,对于上层使用的软件来说,可以做到完全无感,这也是上面非 assets 目录下 PDF 标注功能能使用的前提。正因为之前使用过这个功能,所以在考虑插件同步方案的时候,我第一时间就想到了是否可以尝试用软链接来解决呢?答案是肯定的。
对于 Logseq 来说,基本的配置信息都在用户目录下的一个隐藏文件夹 .logseq
中,如图:
其中, plugins 目录存放的就是我们下载的各种插件,以及各种主题; settings 目录则是这些插件的配置信息,而 preferences.json 这个文件则记录了我们的各种配置信息,包括使用的是什么主题、启用了哪些插件等等,graphs 文件夹存放的是各个笔记库的一些数据索引信息,通常我们不需要关心,git 文件夹是开启 git 自动提交使用的,也不需要关心。我们只需要确保 plugins settings 和 preferences.json 这几个文件和目录在不同电脑上内容一致即可。
这里我的思路是,将这几个我们需要同步的目录内容拷贝到自己的 Logseq 文件夹中,使用 git 进行同步,然后在每台电脑上,使用软链接将 .logseq
中这几个文件关联到我们 git 仓库中这一份,这样就做到了不同设备之间的插件和配置同步,流程大概长这样:
确定了方案之后,具体的执行其实就很简单了:
.logseq
中的内容拷贝到你的 git 仓库中,移除 graphs git 这两个我们不需要的目录。.logseq
中 plugins settings preferences.json 文件.logseq
目录中,创建对应的软链接命令如下
1 | ln -s xxx/.logseq/preferences.json preferences.json |
把其中的 xxx 替换为你电脑上 git 仓库的目录即可,之后在新的设备上重复上述操作。
至此,我们就实现了多设备间插件、主题以及用户配置的同步,而且继续保持我们的优良传统,免费自建,希望本文对你有所帮助~
]]>目前 Logseq 针对各种文档、图片等外部资源的管理形式比较简单,统一放到 assets
这里目录下进行管理,在实际使用中通过相对路径进行引入,大概是下图这样:
可以看到,这里面包含了 PDF 标注文件、音频文件、视频文件、各种图片资源等,这样做的好处非常明显,可以统一管控全部资源,做到各平台体验一致,包括在移动端上也能够正常查看图片、音频等,例如下图的思维导图,引入之后,点击链接电脑上可以打开进行文件编辑等,比较方便。
同样的,这样做的缺点也比较明显,你在 Logseq 中使用的全部资源都会拷贝一份到它自己的资源目录中,如果是截图这种偶尔使用一下的资源还好,如果是工作学习中使用到的文档,就会很不方便,无法进行管理,毕竟你并不是只在 Logseq 中使用这些文档。
首先,Logseq 本身并不是一个用于管理文档的软件,只有最基本的检索和打开功能,一旦文件变多了之后,没有目录层级、没有标签,文档的检索会成为一个很严重的问题,当然你可以通过单独建立一个 page,把相关文档都放在其中,以实现打标等功能,但操作路径会比较别扭且略有成本。
同时当你拖拽文档进入 Logseq 之后,你的电脑中存放了两份相同内容的文档,且以后你只能以 Logseq 中这一份为准,之前的拷贝必须删掉,不然你就会面临更新编辑无法同步到 Logseq 的问题。之后,当你想要进行文件编辑的时候,只能通过 Logseq 点击打开进行,因为当你的 assets 文件夹中包含了成千上万截图、文档资料的时候,你几乎不可能在文件夹中进行查看管理。
有时候你可能并不是想编辑,而仅仅是想要分享给同事,你会发现,如何在本地文件中找到并发给他并不容易,我目前想到的路径是找到该资源对应的 block,点击查看 Logseq 进行改动后的文件名,然后把相对链接拼接你本地 Logseq 的路径,转换成绝对路径,这样就可以在系统资源管理器中打开了,毫无疑问,这个操作成本非常高(这里也可以考虑使用一些类似 Spotlight 或Alfred 这样的软件进行文件索引,相对成本低很多)。
第二个问题,就是随着你的长期使用,assets 的大小会逐步膨胀,这在多设备之间同步的时候就会慢慢变成一个你无法忽视的问题。如果你用的是同步盘,这个问题还稍微好点(不过同步盘自身就有很大问题)。如果你像我一样,使用的是 Git 同步 (参考
Logseq 系列之 Git 同步 | samwei12’s blog ),你会发现随着你的使用,你的 Logseq 文件夹膨胀速度比你想象中的快很多。这是因为 git 本身的原理是基于记录你每次提交的文件变化来进行跟踪,当你使用文本文件的时候(也就是非二进制文件),通常每次提交 git 会准确的把其中几行改动记录下来以便进行版本管理;但当你使用的是二进制文件,由于本身比对改动非常不方便(即使把改动的二进制变化贴给你你也看不懂,自然也就失去了意义),所以很多时候都会直接把改动前后的版本都记录下来,这也就意味着,假如你有一个 100MB 大小的 PS 文件,即使你只改动了其中一个像素,那么提交的时候也会把改动后的这 100MB 大小的新文件提交上去,不难想象,随着你改动的文档以及频次变多,你的 assets 文件夹会轻松变为一个几十 GB 的庞然大物,让你的同步变得非常困难。
注: Git 本身提供了对于大文件管理的专用方案,Git Large File Storage | Git Large File Storage (LFS) replaces large files such as audio samples, videos, datasets, and graphics with text pointers inside Git, while storing the file contents on a remote server like GitHub.com or GitHub Enterprise.,但有一定的使用成本。
那么如何解决这些问题呢?首先,我们需要一个专门的文件管理软件,它能够进行正常的文件目录、标签、检索、预览编辑等操作,同时,我们还需要能够在 Logseq 中插入对应的文件链接,以便在笔记中进行引用。
针对上面的诉求,我第一时间想到的就是 DEVONtechnologies | DEVONthink, professional document and information management for the Mac and iOS 这款软件,它同时支持苹果全家桶,是专业的个人知识文档管理软件,不光能够进行检索、标签,甚至还可以进行网页抓取,防止网址失效,可以说是目前 Mac 平台里面最专业的最好用的管理工具了,唯一的问题大概就是价格过于高昂,当然可能这不是它的问题。
有了文档管理软件之后,我们再解决文件链接引用的问题,这里我们引入一个术语,URI scheme, 可以通过 List of URI schemes - Wikipedia 了解一下, 简单点来说就是一种约定好的资源标识链接格式,通过这个链接以及一些约定好的参数,就可以通过跳转链接来唯一定位软件中资源,然后就可以实现一些功能。 最常用的例子就是文件管理器中的文件路径,例如,文件管理器规范为 file://[host]/path
, 这是大家统一约定好的,路径大概长这样 file:///Users/samwei12/Downloads/docker/Dockerfile
,通过这个路径就能唯一定位到文件夹中的这个文件。
通过 URI Scheme,我们就可以在 Logseq 中引入文件链接来做到点击跳转,跟普通使用一个网址没有什么区别,就像这样:
同样的, Devonthink 也支持 URI Scheme,而且支持的更好。它的 Scheme 格式长这样 x-devonthink-item://C81BB953-8659-4AE7-9E89-143C4F6036BC
,看起来就是普通的 UUID,但它比 Finder 强大太多了,除了能够直接点击定位以外,它最大的优势就是可以在文件位置变更之后依然能够定位到文件!有没有感觉很棒? 试想一下,当你的文件夹位置稍微有所调整,所有已经引用的文件路径全部作废,有没有很难受?这时候你就会发现 DevonThink 是多么的强大,甚至改了名字之后,这个链接依然有效!包括搜索的时候,不光可以名字搜索、标签搜索、文件夹搜索,还会根据你的文档使用和打开频率给他们打分,把分数高的排在前面,非常的方便。基本上使用了它之后,你就不会再想用系统文件夹进行文件管理了。
那么我们的第一个问题,文件资源管理器以及引用就解决了,那么现在来解决第二个同步问题,这里目前我的解决方案还不是很完美,但基本满足我个人的诉求了。
DevonThink 支持两种方式建立索引,方式一是 Import,也就是类似 Logseq 这种,把文档全部拷贝到自己的管理系统中统一处理,这样做的好处就是可以使用它自己的一整套管理体系,包括数据同步、客户端等,但也会受限于它的服务器连接速度,可以说各有利弊,毕竟自由控制跟统一管理总是没法同时满足需求的,想要做到统一的体验,就需要牺牲一些东西。如果你不愿意把自己限定在 DevonThink 的体系里面,它很人性的提供了方式二,也就是 Index 这种,它的使用方式是你自己提供一个已有的本地文件夹,它仅仅通过检索文件信息来进行管理,这样做最大的好处就是既能够享受它强大的标签、索引系统,还可以自己本地保留管理的权利,不必 Lock-in 在这个软件里面,如果你选择了方式一,想要自己手动进行管理会麻烦很多。
我选择使用方式二,我借鉴了 PARA 的概念,本地专门有一个名为 Resources 的文件夹,它的作用就是用于管理我的全部个人文档,可以使用树状目录,也可以使用 DevonThink 的标签系统,或者 Finder 的标签系统,然后使用方式二把它添加索引,拷贝链接到 Logseq 中进行使用即可。如果你需要的话,只要把这个文档文件夹使用同步盘进行同步,就可以做到跨电脑使用,而 Logseq 目录中仅包含文本文件,体积非常可控。不过日常使用中为了方便,图片资源我还是都使用 assets 文件夹,而 pdf 文件,我有单独的管理方式,也可以认为不在 assets 中,后面有机会再分享。这样做的好处就是我整个包含了全部笔记的 Logseq 文件夹,也就不到 500MB,对 Git 的压力就非常小了。
当然,有利就有弊,方式二最大的问题就是多平台体验会不一致,由于文档文件本身还是在电脑设备上,通过云盘还可以做到跨电脑使用,但移动端就不可用了,原因是 iOS 端苹果的系统限制,软件通常只能访问自己应用目录下的文件,无法跨应用进行访问,DevonThink 移动端也只能访问方式一那种自己管理的数据,不过由于我个人在移动端仅仅是查看笔记居多,几乎也没有这方面的诉求,所以这个影响可以忽略不计。
这里不得不赞一下国外软件的开放特性,通常大家都是在思考如何把自己的功能做的标准化,可以让更多的软件融入进来,而国内软件大部分都是思考如何让自己的软件封闭化,全部功能只靠自己就可以做到,好像做开放一点自己的用户就会被其他人抢走一样,思考一下你平时用的国产软件有多少支持 URI Scheme?又有多少国外软件支持 URI Scheme?
扯远了,回到正题,DevonThink 有一款 Alfred 插件。Alfred 同样是一款非常棒的软件,类似于 Windows 下的 everything,但功能要强大太多了。不光可以替代掉各种剪切板软件、计算器、文本替代软件,还提供了非常棒的插件功能。
安装了这个插件之后,可以直接在 Alfred 中输入 dnt 或者 dnw 缩写,即可实现与检索本地的 DevonThink 软件相同的功能,非常方便,详见 mpco/AlfredWorkflow-DEVONthink-Search: Powerful Tool for Searching in DEVONthink. 。
至此,本文就介绍完了我自己如何在 Logseq 中进行文档的管理和使用,虽然是依托了 DevonThink 这个非常棒的软件,但整体思路是不受限于此的,但凡是可以提供稳定 URI Scheme 的软件均可以替换掉它,比如 Obsidian 也很棒。只是说在文档管理本身上功能略有偏差,但对于减负 Logseq 来说足够用了,再配合一个同步盘,可以做到多设备同步。
如果本文对你有所帮助或者你有其他方式,欢迎留言交流~
目前整本书还没有读完,我挑了其中个人比较感兴趣的职业篇和学习篇阅读完了,其中职业篇有介绍到设置一个人生目标,而学习篇则介绍了作者本人总结的一套学习方法,他称之为 “十步学习法”,本篇文章主要就是我自己使用十步学习法进行个人目标设置和规划的一些实践。
作者说自己 33 岁就已经实现了财务自由,且同时还是一个健身博主、营销专家,本书的序其中还有 Robert C. Martin 这样的大牛,说明作者本身在编程领域也有很深的造诣,这些跟他自己的学习方法关系密切。同时他还利用自己总结的这套学习方法在一年内开发了超过 30 个适合开发人员的长期培训课程。当然,很多这种所谓的成功学都是有很多水分的,我们只需要借鉴其中个人觉得有价值的东西即可。
这里我先直接上一张书里面的图:
十步学习法的基本思想就是先对自己所需要学习的内容有一个基本的了解,知道自己有哪些不会的知识点,再根据这个圈定好的范围明确目标和计划的制定。
整个方法分为十步,所以被称为“十步学习法”,其中,1-6 是一次性解决的问题,7-10 则是需要重复训练学习的部分。从整个大的流程来看,十步学习法跟 PDCA 工作法或者费曼学习法有很多共通之处,作者应该也是受了相关方法论的影响总结出的更加适合自己的方法。比如在学习之前,先明确自己的整体目标,开始学习之后,不要一头扎进去,而是要边玩边学,把自己实践过程中遇到的问题记录下来,汇总部分问题之后再回头看书学习解决问题。这与 PDCA 中,制定计划、执行、定期检查的流程基本类似;而十步学习法的最后是乐为人师,这应该也是借鉴学习金字塔或者费曼学习法而来,掌握知识或者技能的最好的办法就是教会他人自己所学到的东西,既可以在整理准备的过程中回顾自己学到的内容,又可以从他人的反馈中知道自己哪些地方还没有吃透。
对我个人而言,这套方法中最有价值的地方在于作者单独把 1-6,也就是如何制定目标和计划的部分加以抽象总结,这是我之前未在其他方法中看到过的。尤其是其中关于学习资源的部分,非常适用于当前这个知识大爆炸的时代,很多时候,人们并不是找不到资源,而是资源实在太多了,很容易淹没在信息大海里面,忘记了本身的初衷。
我自己最近在思考的一个个人目标就是如何健康的长寿,不光要活得健康,最好还能活得久,读了这本书刚好练习学习方法的时候制定一下自己的长寿计划。在具体练习的过程中,我发现十步学习法稍微有点冗余。每次制定学习计划时,如果都 1-10 这么来一遍,过于繁琐,且很多时候你会发现按照 1-10 这种方式挨个执行,自己都不知道自己当前在第几步,这里指的主要是前面 1-6 的部分。 比如了解全局,如何了解呢?大多数人肯定还是网上搜索资料,那这一步跟 4 寻找资源有啥区别和联系? 2. 确定范围 跟 3. 定义目标,本身感觉也容易边界不那么清晰。当然,作者本人也提到,并不一定非要按照他这个步骤来,这些步骤本身并不重要,重点是读者需要开发出一套适合自己的自学体系。
我把这十步划分为了四个大的部分,这样边界更加清晰,比较适合操作,跟原有的方法对应关系如图
你不知道哪些你不知道的事。
这里包括原方法的 1-4 以及 6,重点是如何快速了解一个自己不知道或者不熟悉的主题,并且能够制定一个有明确目标的学习计划。我把筛选资源这一步也归类到准备工作中,原因是我个人觉得,在检索资源的同时,就应该利用自己的鉴别能力对资源进行筛选,只利用其中优质的资源进行计划的设定,把筛选步骤放在前面好过制定完学习计划之后发现这个信息源不靠谱,需要推倒重来。
为了能够制定一个学习计划,我们需要明确以下几个问题:
以健康长寿计划为例,这里首先需要明确需要研究的主题是什么,此时因为对主题缺乏足够的了解,所以有一个模糊的概念即可。我比较关心的是如何健康长寿的生活,所以直接检索 “长寿” “健康” “不生病” 等关键词,以便对于这个主题有一定了解。
关于信息检索,由于目前互联网发布信息的门槛过低,以及大量 内容农场 - 维基百科,自由的百科全书 的存在,很有可能你检索到的信息会把你往沟里带,具体例子可以参考 魏则西事件 - 维基百科,自由的百科全书,这里强烈推荐使用 Google 或者 DuckDuckGo 引擎,同时,如果你英文水平尚可的话,优先使用英文进行检索,尤其适用于一些专业度非常高的主题(比如计算机、医学等)。
在你检索得到一大堆结果之后,如何快速获取自己需要的主题信息以及如何进行筛选是另外一个很重要的工作。目前网络上的资源包括各种自媒体文章、视频、音频、书籍等等,很容易就让你眼花缭乱,忘记了自己想要做什么。在这里,我的一个小建议是,优先选用一些领域专家出版的书籍作为信息源,而不是各种自媒体文章或者其他信息。这样做的原因包括:
另一个关于了解主题的建议是,快速翻阅找到的这些书籍的目录。通常来说,作者在编写书籍目录的时候都是经过深思熟虑的,某主题的书籍目录,往往涵盖了这个主题大部分内容,且一般是由浅入深,带你逐步了解熟悉这个主题。所以翻阅目录能够让你在最短的时间内知道这个主题所涵盖的大部分知识,便于你选取自己的学习目标,制定计划。
拿我研究的长寿计划举例,在检索了相关关键词之后,我得到了一大堆文章,同时,我还在豆瓣、亚马逊等地方检索有关这些关键词的书籍,得到了一堆书籍,包括《管理你的健康》、 《人体的故事》、《端粒》、《长寿》、《人为什么会生病》等等。通过翻阅这些书籍的简介和目录,我大概知道了影响一个人健康和长寿的主要因素包括身体健康和心理健康,涉及到了医学、心理学、营养学等等内容。
之后就是对这些资料进行筛选,同时慢慢对自己要研究的目标有了逐步清晰的认识,同时从 geekan/HowToLiveLonger: 程序员延寿指南 | A programmer’s guide to live longer 了解到了什么是全因死亡率。同时根据个人情况逐步对已有资料进行剔除,比如自我感觉心理状态还可以,可以先跳过心理健康相关书籍和资源;目前还比较年轻,同时也没有家族遗传病史,所以疾病学相关知识也可以先略过,等以后再研究。最终,明确我的目标变成了制定一个降低自己全因死亡率的初步计划(暂不包括心理健康、疾病学相关主题)。
检索资料和筛选的过程,就是逐步发现自己的目标,一步步靠近的过程,这里最实用的原则就是 ”二八定律“(也就是帕累托法则 - 维基百科,自由的百科全书)。个人精力是有限的,你不可能做到面面俱到,尤其是在你对一个新主题不了解的情况下,这时候如何根据自己当前的情况,明确自己下一步的目标就非常重要了。既能够节约你大量的时间,又能够让你摆脱知识焦虑。当你逐步达成了现有的目标之后再回过头来查漏补缺,对这个主题其他目标发动攻击。
当你有了一个比较清晰的目标之后,接下来就是要制定一个可执行的学习计划,目标就像是航海中的目的地,而计划就是航线,没有目标或者航线你就会迷失在大海之中。制定计划的过程中最重要的原则就是 SMART原则 - 维基百科,自由的百科全书,其中我感觉最受用的就是可衡量 M、可达成 A、和时限 T。时限,不用多说,通常情况下,你的学习计划都需要一个截止时间,所谓的 deadline 是第一生产力一定程度上非常准确,大部分人制定的计划也都会包含这一点,但很多计划中缺失了可衡量和可达成原则。可达成不必多说,如果你给自己设定的目标压根完不成,那计划自然也就失去了意义,比如你希望自己长生不死,这违反了已知的自然规律,自然也就无法执行落地。可衡量的重要意义在于你需要明确的知道自己到底有没有达到这个目标,比如说你的目标是锻炼,那么每周锻炼三次或者今天锻炼 45 分钟都是一个非常好的目标,今天练到爽就是一个不可衡量的目标,因为爽的定义非常难,你今天觉得练得很爽,下次相同的练习就未必了。
降低自己的全因死亡率显然只是一个很粗略的目标,我们需要更加详细的执行计划,不过由于这个目标的特殊性,它并不是每个阶段产出结果的,我们只需要确保过程中的小目标都是可衡量,有时限的即可。
我自己的计划设定中,很多信息都是来源于 geekan/HowToLiveLonger: 程序员延寿指南 | A programmer’s guide to live longer ,推荐大家也都去了解一下。结合我自身的情况,以及是否可衡量,对已有的这些影响全因死亡率的因素进行过滤。比如,其中吸烟、喝酒等习惯我都没有,可以忽略;刷牙由于目前基本每天执行,也没必要放到计划中;同时,我尽量挑选那些影响比较大的因素(二八定律),所以少喝或不喝甜味饮料、多吃植物蛋白等也排除掉。得到一个可执行的计划:
有了一个可量化可执行的计划之后,接下来还需要解决一个很重要的问题,那就是目标追踪,也就是说你如何跟进你的进度,如何知道自己什么时候应该调整计划。现在这类目标跟踪软件网上非常多了,我自己最近在使用 Vision,这个是买断制软件,同时支持 iOS 和 Mac平台,买断价格是 88¥,感觉还是很良心的。
我选择这款软件的原因有:
不过这个软件针对我目前这种需要重复完成的目标支持的不是很好,比如我想要完成每天早起的目标,因为目标必须要有起止时间,那么我还得计算这个周期内总共有多少天,我需要完成多少次,略有点繁琐,除此之外整体使用体验还不错。
这部分另外一个重要的内容就是在学习和实践中掌握平衡,反复练习,这里不得不佩服孔子在两千多年前就提出了”学而不思则罔,思而不学则殆“,理念十分类似,类似的理论还有王阳明提出的知行合一。 如果明确目标之后,就一头扎进去看书学习,而不进行实践,你会发现自己很容易学完就忘;而如果稍微看一点就开始不断尝试,你会花费大量的时间,很可能尝试了几天都未能解决的问题,就在你要学习的下一章提到了。所以如何边学边练是这一部分的重中之重。
对于降低全因死亡率这样的计划来说,并不能像学习新的编程语言或者学习如何搭建一个 SpringBoot 应用实操性那么强,不是很能体现学习和实践的紧密关系,但也是适用的。经过初步的了解之后,可以深入到各个子主题里面学习,比如如何提高自己的睡眠质量、如何提高自己的心肺功能、如何增肌等等,更具体的例子是学习了一个新的健身动作,如何通过看视频、听教练指导一步步矫正自己的动作,过程中你还可以通过录像查看自己的动作是否标准,并与学习视频逐一对照修改,直到动作十分熟练,形成肌肉记忆。
跟原有方法保持一致,参考费曼学习法 - MBA智库百科,比如通过写博客的形式把我自己的实践经验分享出来,本文不再展开。
这次的研究课题其实不是很适合用这个学习法,实操性比较差,后面的步骤介绍的不是很好,不过十步学习法中的重点还是在于如何检索资源、筛选资源,并且利用二八定律快速制定一个可执行性较强的学习计划,关于这部分,我感觉还是非常有用且在我制定自己计划中起到了重要帮助的,把之前可能已经在用的一些思路、原则很清晰的提炼总结了出来。
希望也能对你起到一定作用~
随着 Logseq 的 iOS 版本发布及更新,手机上也能够快速的访问甚至随手自己的笔记了,这无疑是一个非常棒的事情。当然,由于 iOS 系统本身的特点,我们只能使用iCloud 进行 PC 和手机之间同步,这本身并没有什么问题,甚至为了手机端同步,我花了一点时间把放到自己 NAS 里面的笔记全部迁到了 iCloud,并且重新订阅起了 iCloud 的服务(每月 6 元最低档)。
然而,经过一段时间的使用,我发现事情并没有想象中的那么简单。
使用云盘这类服务首先需要面对的一个问题就是版本控制,当然不是说云盘无法做到版本控制,目前大部分云盘都已经支持历史版本(虽然有不少都是需要额外付费),其中做的比较好的当属坚果云,可以比较方便的查看某个文件的多个历史版本,可以恢复到指定版本。由于个人原因,我无法使用坚果云同步笔记,在迁移到 iCloud 之前使用的是自己的 NAS 同步,基本也能满足需求,但也只限于基础需求了。如果我想知道今天新增了哪些笔记、 某个笔记最近都修改了哪些内容,那么普通的版本管理就很不够用了,更别提 iCloud 的版本管理甚至不如 NAS 的同步盘。
如果仅仅是这个问题,倒也还能忍,但 iCloud 时不时的文件冲突就让人很烦躁了,而且这种同步盘有一个设计感觉使用起来很难受,那就是文件冲突的时候并不是提示你修复冲突,而是自作主张就帮你选好了一个版本,同时舍弃的版本还重命名一下,于是使用一段时间之后你就会发现文件夹里面多了 n 多相同前缀的重复文件,后缀以不同时间戳之类的命名。
iCloud 另一个非常让人难受的地方就是同步稳定性,照理说服务器都在国内,同步速度应该非常顺畅才对,但有时候它的同步逻辑就非常迷,就是有个别文件迟迟不同步,什么时候同步看它心情,这方面网上文章非常多了,可以搜一下解决办法。但偶尔在手机上查看某文件的时候发现不是最新就非常难受了。
其他一些易用性的问题也非常多,比如,不希望同步指定文件夹,例如 Logseq 的 bak 文件夹,或者部分隐藏文件等,在 iCloud 或者一般的同步盘里面都是非常难看到的功能,顺便一提,NAS 同步盘倒是支持,而且还支持通配符,很不错。
总之,云盘的同步功能比起 git 来简直是天上地下,如果你对 git 稍微有点了解的话,应该知道版本控制、断网提交、忽略指定文件等等在云盘上非常难看到或者难用的功能在 git 这里可以说是最基本的本领了,在官方同步版本迟迟未更新的时候,对于稍微有点动手能力的同学来说,选择使用 git 无疑是一个非常省心的决定。
不了解的朋友可以看下官方介绍,对于人均程序员的 Logseq 用户来说相信不存在这个问题~ About - Git
接下来我们聊一下如何搭建一个 git 服务。
最简单的方案就是使用现成的免费服务,例如 GitHub, 或者 The One DevOps Platform | GitLab 以及 Gitee - 基于 Git 的代码托管和研发协作平台,对于有能力使用外网的同学,推荐使用 GitHub,相信网速也并不是问题,愿意选择国内服务的话,gitee 也还不错。这些服务也都支持个人免费使用,同时支持私有仓库,也就是说你推送的内容,除了你别人都看不到。
但有部分人会担心自己的笔记放在他人的服务商会不安全,存在隐私泄漏问题,尤其还是个人笔记这种十分隐私的内容。我自己也是这么想的,所以我选择第二种方案,自己搭建一个 git 服务器。
搭建一个 Git 服务器非常简单,甚至你安装好 git 命令行之后可以在几分钟之内搭建一个自己本地的仓库,从笔记文件夹提交到另一个仓库文件夹中。由于本文篇幅有限,就不展开介绍 git 命令以及搭建了。我自己由于家里有一台 NAS,本着资源利用最大化的思想,就在 NAS 的 Docker 中安装了 Gitea, 这里你也可以选择使用 Gitlab 等服务,群晖系统可以直接在套件中心下载安装,比较简单省心。 但我个人比较推荐使用 Gitea,除了它是开源免费以外,最主要的原因就是轻量级,它的功能基本已经能够满足小型团队所有的日常开发工作,包括权限管理、自动化等等,这种情况下它的安装包只有 148 MB,占用的内容更是非常少,如果换成 Gitlab 这种庞然大物,起码是 2GB 内存起步。
这里简单说两个安装过程中遇到的问题吧,使用 Docker Compose 安装的话,mysql 的创建速度会比较慢,此时直接配置 gitea 会导致报错,提示无法连接 mysql。最简单的解决方案就是安装的时候多等一会,可以通过执行 docker logs -f xx
查看 mysql 的日志输出情况,提示 mysql 创建完成之后,再进入到配置页面进行配置。
另一个问题就是关于 ssh 配置的问题,为了安全起见,ssh 端口号最好不要使用默认,而是自己改一下,注意修改完之后,docker 配置里面必须使用相同的端口号,且域名中不能带 http,这样会导致克隆的时候没有办法使用 ssh,只能使用 http 方式。
首先你需要一个 Git 仓库,有了服务器之后这一步非常简单了,比如直接在 GitHub 或者 Gitea 的控制台页面,新建一个仓库即可,然后这里有个两种方案:
方案一: 可以先本地克隆一下仓库到一个新的文件夹中,例如,PKM, 然后把你之前的所有笔记拷贝到这个仓库中,注意,直接从根目录进行拷贝也就是说,PKM 的文件夹里面长这样
之后,选择提交本地改动到远程,就可以了。 注意这里挪动过文件位置之后,需要打开 Logseq 重新定位一下新的库。
方案二: 如果不想 reindex 的话,可以直接打开 Logseq 配置中的版本管理,Logseq 会自动帮你创建一个本地仓库,之后再把这个本地仓库关联到自己的远程仓库即可。
如上图,打开这个开关,然后重启 Logseq,此时,进入到你的笔记文件夹中,打开显示隐藏文件夹选项,你应该能看到一个 .git
文件夹,说明已经帮你创建好了一个本地仓库,然后使用命令行,进入这个文件夹, 此时输入 git remote -v
应该没有任何反应,之后我们需要把这个本地仓库关联到我们在控制台创建的远程仓库, 输入命令 git remote add orgin 你的仓库地址
即可,之后就可以正常的进行拉取推送了。
如果你是安卓手机,那么可以跳过这一章,直接开心的用起来就好了,这里推荐使用 Termux。
好了,目前你已经有了一个自建的 Git 服务,也能够在电脑上编辑完之后提交推送到远程仓库里面,那么如何在 iOS 上拉取到最新提交呢?
由于 iOS 的文件夹权限限制,正常的 APP 都只能在自己的创建的文件夹中进行文件操作,除非是 Logseq 自带 Git 支持,否则是无法使用 Git 服务的。我之前也被这个问题困扰许久,直到了解到了 Working Copy 这个神器。 在这里必须要隆重介绍一下这个 Git 仓库 CharlesChiuGit/Logseq-Git-Sync-101: This repo aims to help Logseq users to sync their data with Git and GitHub., 作者详细的分析了各种同步方式,介绍了 Git 的使用,以及整个工作流,可以说本文大部分内容都是基于这个仓库的,十分感谢原作者 CharlesChiuGit。
Working Copy 这款软件从 2014 年就上架了,它里面最厉害的一个功能就是能够同步自己内部的一个文件夹和其他 APP 的文件夹,并且这种同步可以做到双向,这样每当你利用 Working Copy 进行 git 拉取的时候,它会自动更新 Logseq 里面的那一份,反之,每当 Logseq 中的内容进行变更的时候,它可以将变更提交到远程仓库。
整个流程步骤如下:
其中,3.4 的顺序并不固定,如果你的仓库中已经提交好了笔记,在 2 这里直接拉取也可以。
需要注意的是: Working Copy 链接外部文件夹功能是一个付费功能,但作者非常良心的提供了 10 天的试用期,所以如果只是在手机上查看而不是编辑的话,你可以利用试用期把需要的仓库都克隆到手机上,之后即使试用过期,你也仍然能够使用已有功能。
虽然可以免费使用,但还是建议大家购买正版支持一下作者,终身买断 128 人民币,并不是很贵,而且免费版是无法进行推送的,如果你经常使用 Flashcards 功能的话,就会发现手机上进行复习是非常方便的,这时候不推送是无法保存手机学习记录的。
到这里,你已经可以愉快地在手机和电脑上都用上 Logseq 了,并且 git 同步非常的稳定,再也不会出现想要的文件不同步或者是重复文件的情况了,你还能获得一大堆其他 feature, 比如在 .gitignore
中简单配置下面这两行就可以确保不会每次同步一大堆废弃的文件。
又或者,你可以查看自己每天更新的笔记情况,甚至是每个文件的改动情况。
更高阶的玩法包括回滚某次提交、 恢复到指定版本等等等等。
但还有一点点可以提效优化的地方。首先,如果是切换设备之前先在设备 A 提交一次,再去设备 B 拉取一次,那肯定是非常难受的,毕竟连同步盘都能做到后台自动同步呢。所以我们要先把提交变为自动化。
这里也有两个方案,方案一是通过脚本定时提交,参考 git-auto/git-auto at master · logseq/git-auto, 用起来也很方便,开机之后跑一下脚本就可以了,有一点可以改进一下的就是这个脚本通常只会自动提交并 push,如果切换了设备,是不会自动拉取最新版本的,可以在 66 行这边补一下拉取更新的逻辑,每次提交前先拉取最新版本,就更加好用了; 方案二更加方便,在仓库的隐藏文件夹 .git
中找到 hooks ,从 CharlesChiuGit/Logseq-Git-Sync-101: This repo aims to help Logseq users to sync their data with Git and GitHub. 中下载 git-hooks 中的文件并复制到 hooks 中,记得使用 chmod +x ./pre-commit && chmod +x ./post-commit
将文件变为可执行文件,这样 hooks 才能生效,之后在 Logseq 的设置中打开版本控制,开启自动提交,重启 Logseq 即可。它的原理是利用了 Git Hooks | Atlassian Git Tutorial 的能力,在每次自动提交之前拉取一下远程仓库,同时在每次自动提交之后把改动推送到远程仓库中,同时 Logseq 的这个配置项开启之后,内部会启动一个定时器定期检查是否有可提交改动,如果有则触发 commit,如果没有改动则不做任何事情。 个人更加推荐使用方案二,相对来说更加自动化一点,且拉取跟推送的问题都解决掉了,非常完美了。
另一个可以优化的地方就是 iOS 的操作了,毕竟手机上并没有类似电脑上自动 commit 的功能,每次打开 Logseq 之前还要先去 Working Copy 中选中仓库,然后点击拉取,用完了点击提交推送,还是很麻烦的。我们可以通过系统提供的捷径功能简化操作,不需要打开 Working Copy的情况下,把提交和推送都完成。
Working Copy 提供的捷径已经非常简单易懂了,我这里直接上两个截图,分别是拉取更新和提交更新的步骤,正常没有冲突的情况下基本都够用了。
如果有冲突需要解决,最好还是去 Working Copy 里面进一步进行处理。
之后再把这两个捷径添加到桌面上,放到 Logseq 旁边,这样,每次打开 Logseq 前点一下拉取,编辑完再点一下提交,能满足绝大部分需求了。
至此,整个 Logseq 的 Git 同步方案就完成了,应该说如果跳过自建 Gitea 的步骤,还是比较简单的,但用上之后就真的停不下来了,可以说好的同步方式能够解决大部分 Logseq 的使用问题,让 Logseq 变得更加好用~ 希望本文能够对你有所帮助。
]]>不过实际按照教程配置的过程中,发现原作者是基于坚果云搭建的,不符合我本地化的思路(主要是公司不允许用云盘),因此尝试摸索一个本地任意文件夹均可实现的方案,过程中踩了非常多的坑,记录下来,希望对大家有帮助。
因为原文已经写的非常详细了,所以这里我不再一一列举配置,而是仅把本地化配置需要注意的事项标注出来。
这里坚果云不再是必选项,而是任意本地目录即可,注意必须要保证目录结构为 xx/SimpRead
才行,这个是硬性要求,否则会无法使用简悦的本地知识库功能。其实只需要在同步文件夹里选择一个名为 SimpRead 的文件夹即可,导出部分使用默认
详细服务设定参考原文,全部打开。
关于简悦知识库相关内容,可以查阅官方文档 建立知识库
全文(含标注)+标注
这里是配置大头,也是容易出错的地方,重点说一下。
【授权】
【定制导出】
{{id}}{{un_title}}{{mode}}
(这个主要是为了后面稍后读能够加载本地文件)1 | tags:: #[[SimpRead]] {{tags}} |
主要改动包括:
效果图:
这里一定要注意,[[{{date_format|now|yyyy-MM-dd}} ]]
后面是有一个空格的,它不是作者笔误,而是必须要这样才能正常添加时间戳,参见: 定制导出 ,
这里有一个坑花了我三天时间,必须要重点讲一下。
但这里更建议是保存为 Textbundle 格式,因为离线 HTML 需要将图片转译为 Base64 的代码,碰到多图杀猫的文章很容易卡顿或失败。
因此在自动化中,我导出了 HTML、Markdown、Textbundle,结果就是,尝试了各种方案,包括数次卸载重装、排查本地端口占用、卸载同步助手重装等,都无法成功在本地使用 http://localhost:7026/reading/
跳转文件,一直提示 404,,中间尝试了各种方案,一度怀疑是否只能使用坚果云才能用知识库功能。 结果发现全部卸载重装之后,坚果云也无法使用,中间联系简悦作者 Kenshin 排查了很久,最终发现是因为同步助手无法识别 .textbundle
文件,导致不能正常建立索引。
这个问题有两种解决方案: 1. 放弃 Textbundle,改为使用离线 HTML 2. 通过文件规则,将 .textbundle
格式文件移动到其他目录下。我选择了第二种,原因就是离线 HTML 下载容易造成卡顿。不过遇到了另一个问题,那就是 Hazel 实在是太快了,在简悦刚开始创建 Textbundle 文件的时候,就已经把文件夹挪到了其他文件,导致此时简悦正在下载的图片找不到存放目录,引发大量报错。这个问题也卡了我不少时间,具体解决方案稍后单独介绍。
关于这部分,Kenshin 戏称说这个 issue 的长度可以在 2000 个 issue 里面排到前五,过程确实比较曲折,我中间走了非常多的弯路(不然也不会花掉一个周末的时间折腾),一度都想卸载简悦放弃了。有兴趣的同学可以看一下 自定义导出无法使用稍后读加载本地缓存文件 · Issue #3554 · Kenshin/simpread
第二个坑,这里原文作者也有提到,那就是稍后读的自动化流程,必须要在阅读模式下才可以,否则不生效, 参考: 部分操作方式加入稍后读时,无法触发自动化 · Discussion #2362 · Kenshin/simpread
http://localhost:7026/reading/
路径是简悦知识库必须的配置,否则会导致链接无法跳转以及稍后读无法使用本地缓存文件。至此,整个核心配置基本就已经完成了,我们实现的功能包括:
接着上面提到的,详细展开一下如何处理 Textbundle 文件。
首先,我一下子想到的就是利用 Hazel,新建一个规则就可以,当我实际上这么操作的时候,发现遇到了简悦写入图片时文件夹已经被移动导致报错问题。
此时,我的思路是,如果有办法能够延长移动文件夹的速度就好了。但简悦跟 Hazel 是相互不知道的,很可惜的是 Hazel 中也没有配置延迟操作或者定时任务这一功能,那么我们只能想办法自己动手了(当然这里还有一条路,希望简悦后续的文件写入能够先把图片下载完,之后再生成文件夹)。
我想到的解决方案是使用 *nix 系统的 cron 功能,不了解的小伙伴可以看一下 linux - Why is my crontab not working, and how can I troubleshoot it? - Server Fault ,非常详细的介绍了 cron crontab 分别是什么, 如何使用,以及如何排查错误,受益匪浅。
这里需要做的事情非常简单,将所有后缀为 .textbundle
的文件移动到另一个目录中,实际操作起来并不简单,因为 mv
命令无法直接移动文件夹,而 textbundle 其实是一个文件夹而不是文件,包括了 json、图片以及 markdown,所以第一步就卡住了。我改为使用 rsync
来实现这个功能,可以很方便的把某个目录下所有的 Textbundle 移动到另一个目录。之后我们把源文件夹中的 Textbundle 文件删除。这里由于无法使用 Hazel 实现全部功能,那我干脆把移动 @annotate.md
文件的部分也放在自己的脚本里面实现了。完整的脚本文件如下:
1 | #!/bin/bash |
可以直接新建一个 simpread.sh
之后把上面的代码粘贴过去,记得把上面中文说明的部分都替换掉,同时为了手动执行方便,可以 chmod +x simpread.sh
一下。
然后就是使用 cron 让电脑自动执行这个脚本,这里可以使用 Crontab.guru - The cron schedule expression editor 这个网站调试一下定时任务,比如我目前的配置是每隔 5 分钟执行一次脚本。注意不能执行太频繁,否则一样会遇到简悦下载出错(5 分钟也不能保证完全不出错,理论上间隔越久,出错概率越小)。
具体的添加步骤(我是 Mac,以下请在命令行中执行)
1 | export EDITOR='vi' (不是必须) |
提示:
就代表定时任务添加成功,可以把 Hazel 卸载掉了。
看到简悦同步助手里面有如何快速打开稍后读的方式,参见: 稍后读 ,我就琢磨了一下,感觉效率还可以再提高一下。
文档中把快捷方式跟独立窗口分开成两步,实际上 Chrome 浏览器支持直接创建一个独立窗口的 PWA 应用(关于 PWA,参考: Progressive web apps (PWAs) | MDN)。
打开稍后读:
选择独立窗口:
然后你可以在用户目录下找到它
也可以通过 Alfred 或者 Spotlight 找到它
这里不得不强推一个效率软件了,官方网站: Manico - macOS 下的快速 App 切换器
基本上可以说这个软件是我目前每天使用频率最高的软件,应该没有之一,大概是 18 年左右 25¥购入。它的主要作用就是快速切换软件,比如你可以配置 option+1 打开 Chrome,option+2 打开微信等等,而且再次点击快捷键可以隐藏该软件,能够极大的节约软件切换的时间。这虽然是一款付费软件,但绝对是我在 Mac 上买过的最物超所值的软件。
核心配置:快速隐藏应用
如何配置稍后读快速打开隐藏:
我一般的处理流程是手机浏览公众号或者 RSS 初筛到 Cubox,之后电脑上打开 Cubox,跳转到简悦进行深度阅读,添加标注,同步到 Logseq,之后进行定期整理。
这样,整个从文章到标注,到 Logseq 整理的过程就打通了。简悦作为一个稍后读软件,最大的亮点就是作为一个粘合剂,可以很好地跟各种笔记类软件打通,称为知识管理工作流中不可或缺的一部分,把浏览阅读到的信息和知识真正的经过深度阅读,让它有机会融入到已有的知识体系中,内化为自己的东西,就这一点已经远远超越了 Evernote、Cubox、Pinbox 等同类剪藏产品。
不过简悦问题也很明显,太极客了,配置使用成本巨高无比,我身为一个程序员,都好几次尝试无果弃坑了,个人感觉它的功能已经足够强大了,希望作者能够多花点心思在交互跟实际使用体验上,真的不需要再加那么多新功能了,适当的做减法非常有必要。希望我的踩坑经历能够对你有一些帮助。
]]>维基百科中关于心理表征的描述十分抽象,书中的解释是说,专家能够发现很多普通人注意不到的信息特征和模式。直白点说,当你是这个领域专家的时候,对于这个领域的各种知识已经了然于胸,很多事情可以不过脑子就知道来龙去脉。例如,象棋或者围棋高手,能够在棋局很不明朗的时候一眼就看出哪一方会赢;专业的刑警可以通过扫一眼房间就能获取大量的嫌疑人信息。
书中关于心理表征的部分并没有什么特别的地方,但听到这里,我的整个大脑感觉就像打开了某个开关一样,不断的有思绪和灵感涌现出来。
首先,涌入我脑中的是很久之前看到过的一个“DIKW模型”[^1],这里的心理表征不就是Data-Information-Knowledge-Wisdom 中 Wisdom 吗?熟练地掌握各种技能,不光知道如何用,还知道何时使用,内化为个人的某种天赋。
接着想到了“KSA模型”[^2],Knowledge-Skills-Abilities 三层中的 abilities 也是类似的东西,知识-技能-天赋,先学习知识,知道是什么,之后开始练习技能,学会为什么,怎么用,通过不断的刻意练习使用技能,明白何时用,做到手中无剑,心中有剑。
然后回想书中提到的不需要过脑子就本能反应,联想到了《思考,快与慢》中的系统1,这本书中将大脑的思考分为两个系统,一个是靠直觉行事的系统1,比如你看到对面走过去的一个人,立马就能知道他现在是开心还是不开心,通过他的打扮猜测出他的职业等;另一个是靠逻辑思维的系统2,比如做数学题 :)。绝大部分时间,系统2都是不工作的,因为深入思考是需要花费大量精力的,而人的天性是懒惰的,这跟大脑习惯选择系统1是有关系的,尽可能的让系统1工作,让系统2休息,所以很多时候人会做出不理性的行为。比如现在有个赌局,扔一枚硬币,正面会给你130美金,反面你需要给出100美金,你会作何选择,玩还是不玩?绝大多数人的第一反应都是拒绝参与,但冷静下来,通过概率学计算你会明白,你的收益期望是大于 0 的,也就是说你选择玩是有益的,这就是由于没有经过系统2 认真分析,系统 1 由于人的损失厌恶心理而得出了非最优解。
系统 1 和系统 2 并不是泾渭分明的,通过训练,你可以将很多系统 2 原本的工作转换为系统 1 的工作。例如,上面提到的刑警迅速看一眼房间就能获取大量信息的过程,也是他们经过大量的刻意练习才能做到的,专家并不是生来就是专家的。包括《刻意练习》这本书中提到的案例,作者找了几个普通大学生,跟我们一样,扫一眼也只能记几位数字,经过两年训练后,他们居然能够参加速记大赛并且获奖,而速记几百个数字对于普通人来说,绝对是远远超过系统 1 工作能力的事情,这也是典型的通过学习和训练将系统 2 的部分能力转换给了系统 1,这不就是开头所提到的“心理表征”吗。
《思考,快与慢》是一部里程碑式的著作,给后来很多其他心理学相关书籍提供了支持。《穷查理宝典》中提到了查理·芒格所撰写的人类误判心理学,其中绝大部分误判都可以从《思考,快与慢》中找到相关讨论,两本书对照着看理解会更加深刻。
于是,我发现了最近这段时间看到的、听到的各种知识点相互之间居然都能联系到一起,这些知识形成了一张巨大的网,这不是件很神奇的事情吗?
对于脑科学稍微有所了解的朋友应该知道大脑本质就是一个巨大的网络,只有通过神经元之间相互的连接所产生的神奇反应才使得我们能够记忆、学习、思考。所以才会出现上面提到的各个知识点之间形成一张网的情况,因为只有这样,知识才能被更好的记忆理解。而那些孤岛式的知识点早就被遗忘在不知道哪个犄角旮旯了,约等于压根没学过。
人的大脑记忆是存在一个提取强度的,这个概念的意思是说把你的知识点从大脑中提取出来需要花费多大的力气,提取强度越高,代表这个知识点越难被想起来。所以那些使用越频繁的知识、越是最近学到的知识、跟其他已有知识点关联越紧密,越容易被记起来。
说到这里,有没有发现大脑真的很像计算机?计算机会使用缓存把频繁使用到的数据加载起来,所以你会发现经常打开的软件响应很及时,好久没切换过去的软件,处于休眠状态,切换过去的时候通常会先卡顿一阵子,而已经关闭的软件,则需要经历从硬盘加载到内存、缓存的过程,启动时候往往要读条很久(实际上计算机还有不同级别的缓存、寄存器等)。
由此我又联想到了这两年非常火的“卡片笔记法”和双链笔记等概念,仔细思考之后就会发现,他们能火起来的原因就是因为他们符合了人脑记忆学习的过程。卡片盒笔记法中最核心的一个点就是,每新建一个卡片的时候,一定要跟已有的卡片进行连接,这不就是上面提到的大脑记忆东西的过程吗?Roam Research 这样的双链软件最大的特点就是能够配合 daily notes 毫无压力的记下各种灵感,同时只需要打上标签或者说主题,就能够在需要用到指定主题的时候把以往记录的所有灵感想法、收集的素材都关联起来,这不就是通过知识点之间的关联来降低提取强度吗?
这里我想到,关于到底是用文件夹还是用标签来管理文档,网上已经有非常多的争论了,基于上面的思考,由标签系统展开来进行管理相对来说更符合人类大脑的构造,天然的就能减少知识点的提取成本。这里非常重要的一个因素是知识点之间很少有明确的树状结构,大部分的情况下都是网状结构居多,且网状结构也便于回想,不会出现找不到放在哪个目录下的问题,而且大幅度减少了分类的痛苦,大脑天生就不是很适合干这种分类的活。这也是目前流行的笔记软件中,大多都是采用 daily notes 而不是传统的文档型结构的原因,等你想好这个灵感应该归属于哪个目录,这个目录以前是否有创建好等等,你的灵感已经丢失了或者不完整了。
站在更高层次来想一想,不光是人的大脑,整个世界的万事万物,都是相互联系的。这可不仅仅是口头上说说而已,参考“六度分隔理论”[^4],不同的两个人之间都可以通过六个人联系到一起(这个说法存在一定争议),而且随着互联网的发展,地球变得越来越小,这个数字还在不断减少,Facebook 的研究表明,在 2016 年的时候,累计 15.9 亿用户,平均间隔为 4.57 人,仅考虑美国的话,这个数字降到 3.46 人。
这个模型本身是有数学依据的,假设每个人认识 150 个人的话,1506 约等于 11.4 兆,即使消除重复节点,这个数字也远远超过地球上人数的总和。
而在科学领域,也同样是紧密联系的,《穷查理宝典》中查理·芒格提出了著名的“多元思维模型”[^3],简单来说就是你需要掌握多种不同学科的基础理论,它们能给你带来远远超过 1+1>2 的收获,他称之为 lollapalooza 效应。
只掌握一门基础理论是远远不够的,因为“在拿着锤子的人看来,眼中只有钉子”,这会让你的思维方式严重的被局限。
同时,我想到了下午检索了一下现代科学的分支大纲[^5],发现很多学科其实并没有严格的分界线,而是相互之间都有所联系。例如,如果只知道心理学,可以得出结论,人应该如何控制自己的情绪,如何有效的沟通,但只有同时知道了脑科学,知道了诸如额前叶、杏仁体、多巴胺等概念,才能明白人为什么会生气?为什么人一旦愤怒就会失去理智?人为什么会开心?以及它们背后的生物学原理是什么。所以现在阶段的科学研究,通常是多位不同领域的科学家同时进行配合。
以上这些基本都是我在回家路上,脑子里自己突然蹦出来的灵感,一个接着一个,把最近的各种听闻全部串了起来,那种感觉就像是拿到了一把宝藏大门的钥匙。
那么这能给我带来什么启示呢?应该如何保持这种状态?
首先,学习知识的时候不要急躁,不要急功近利。很多人会问读书有什么用?反正看了也会忘。我以前也不知道,现在我明白,那些你学过的东西其实始终都在你的大脑中,只是缺少一个相互连接、爆发的契机。
其次,对学习的方法也有一定的指导作用,需要尽可能的多发挥自己的主观能动性,主动联想,而不是被动学习。每天睡前听 10 分钟单词并不会让你的英语水平有任何提升。这里我又想到了前两天听到的左耳朵耗子在极客时间《程序员练级攻略》中提到的联想学习法,当时并没有太大感触,今天终于才明白他说的是什么。
本以为只是随笔写点想法,不想写长文,结果碎碎念不小心又写了两个多小时,写成了长文,谢谢你能看到这里,与君共勉。
[^1]: DIKW体系 - 维基百科,自由的百科全书
[^2]: KSA: Using the Knowledge, Skills and Abilities Model
[^3]: 多元思维模型 - MBA智库百科
[^4]: 六度分隔理论 - 维基百科,自由的百科全书
[^5]: 科学大纲 - 维基百科,自由的百科全书
这篇文章主要是介绍一下这几年我自己所用过的几款思维导图软件。
思维导图(英语:mind map),又称脑图、心智地图、头脑风暴图、心智图、灵感触发图、概念地图、或思维地图,是一种用图像整理信息的图解。它用一个中央关键词或想法以辐射线形连接所有的代表字词、想法、任务或其它关联项目。它可以利用不同的方式去表现人们的想法,如引题式、可见形象化式、建构系统式和分类式。
以上是维基百科对思维导图的定义,我相信很多人应该都是听说过或者用过思维导图的,思维导图比起传统的文字列表来说更加直观,更加接近人在思考时的空间想象,非常适合用于任务拆解、项目规划、头脑风暴、记录笔记等。比如我我自己在编写文章之前,通常会先用思维导图构建一下整体大纲,明确论点、论据等要素。
在进行软件测评之前,我们还是先梳理一下自己在使用思维导图时候具体有哪些诉求:
iThoughts 可以算是我接触最早的思维导图软件了,大概在12-13 年左右开始使用,最开始的时候感觉还是蛮棒的,颜值一般,但是性能很优秀,编辑撤销、拖拽等都很不错。使用多起来之后发现有点不尽人意了,我当时用的时候只有 Mac 跟 iOS,换 Windows 电脑就无法编辑了(现在已经支持),且样式、模板支持的很少,不支持圈选概要等,逐步不再使用了。
Mindnode 是我在抛弃 iThought 之后接触到的软件,同样也是基于 Native 开发的,这种模式的优势在于能够最好的利用原生系统,例如 iOS 上面的各种手势,同时性能通常都很棒,各种拖拽、编辑撤销都不在话下,体验十分顺滑;劣势就是一般跨平台支持不佳,原因在于每个系统都需要进行一套新的开发适配,Mac 跟 iOS 相对是最好打通的了,但是 Windows 和安卓的支持就比较困难,同时不同系统间差异性很大,想要完成迁移且保持跟苹果系统中同样的体验是非常困难的,我想这也是 Mindnode 至今没有出 Windows 版的原因。
Mindnode 基本上可以说是我用过的导图软件里面颜值最高的,且界面非常克制,简洁的不像话,没有那些花里胡哨的功能,给人十分优雅的感觉。同时,Mindnode 的任务管理是这些软件里面做的最好的,父节点标注为 Task,会直接将全部子节点都标记为 Task,且子节点完成时候会实时更新父节点进度。
很长一段时间内,我都在 XMind 和 Mindnode 之前纠结徘徊,感觉这两款软件都有做的很棒的地方,不过后面还是 转向了 XMind,毕竟国产软件很大程度上是具有先天优势的。
XMind 可以说是国产之光了,目前国内思维导图这块使用者最多,后来者居上,赶超了各种国内外老牌思维导图软件。
我最早接触到这款软件是通过 XMind8,当时还是用 Java 开发的,界面不说丑陋反正也不好看,而且操作很容易卡顿,主要是因为公司内置了这款软件,同事间交流很方便,因此被迫使用,当时的主力还是 Mindnode。
那时候我还不知道 XMind 的开发者在国内,只是突然有一天关注到 XMind 新出了一款替代产品,XMind Zen。最开始确实有点惊为天人的感觉,完全甩 XMind8 不知道几条街,根本看不出来是一款迭代产品,实际上了解下来,基本上可以说是完全重做了,底层的也换成了 electric 开发,最显而易见的好处就是颜值立马在线了,且操作流畅程度大幅度提升,直接进入到了思维导图第一梯队,可以跟 Mindnode 正面抗衡了。
但不得不说,国内互联网开发者推出的产品最大优势就是本土化程度实在是高出国外软件不知道多少,且国内互联网的另一个特点就是拼,非常的拼(可以说深得内卷精髓),产品迭代速度通常都是碾压国外软件,在最近短短一两年内,XMind 就推出了众多重磅功能,Zen 模式、密码设置,还有接下来还即将要推出演示模式,可以说都是非常符合国人诉求的。同时,XMind 中提供了大量丰富的样式与模板,包括矩阵、树状图、时间轴、鱼骨图等等;还提供了一个图库,包含很多非常有用的模板,例如简历、读后感、组织架构图等等,也难怪市场占有率如此之高。
不过 XMind 目前还是存在部分使用上的问题的,我个人比较不爽的就是软件格式的 Lock-In 成本有点高,这也是国产软件的通病。所谓的软件 Lock-in 成本就是指当你想要从该软件迁移到同类其他软件的时候,所需要花费的代价。通常,国外软件不管做的好还是差,一般导出功能都是比较完整的,可以很简单的就将用户数据迁移到其他软件,而国产软件的套路通常是导入功能第一优先级开发,不管你是什么奇奇怪怪的小众格式,只要你有用户,我就支持,且一般支持的都还不错;当用户迁移完毕之后,想要出去就没那么简单了,导出格式较少、bug 很多、导出数据不完整这都算是小意思了,更有甚者,想要导出,可以,买会员先(说的就是你,收趣),真的是把我恶心到了。
XMind 支持的导出格式倒是不少,PDF、Word、Markdown 等,看起来很够用了,不过这里对于图片的编辑导出实在是太弱了。类似 PDF 这种格式就不提了,只是单向导出,并不能进行编辑。你总不希望你以后所有的导图都是一个只读的 PDF 吧。所以我比较关注的是可以双向读写的 Markdown 格式,Textbundle 本质上就是 Markdown+图片。
当你粘贴图片的时候,没有任何问题,但当你想要把图片拷贝出来的时候,问题来了, 你会发现你拷贝出来的内容大概是这样的 [{"src":"xap:resources/4a36ee50ecad8ba5db10a187b23d1024db4c4cbc1527f90fc65251f6499ff926.png","width":756,"height":702}]
,怎么样,是不是看不懂,看不懂就对了,这个路径是 XMind 自己内部的一个资源库路径,对外根本不可读(而且 XMind 至今都不支持 URL Scheme),这时候你能做的就是将这个图片右键另存为,当你发现你的导图里面保存了几十张图片的时候,是不是要死的心都有了?
此时你可能想,我直接导出文件不好吗,年轻人,太天真了,当你导出 Markdown 时候会发现根本没有任何图片,此时你可能想到我上面说的,TextBundle 可以啊,然而当你点击 TextBundle 导出的时候,你会看到如下对话框:
惊不惊喜,意不意外?是不是感觉这个套路很熟悉,好像在哪里见过?于是你不得不接受一个事实,哪怕你可以用它任意编辑,随便白嫖,原本以为只是导出图片有点水印,根本不影响使用,但你想要跳出 XMind 的时候,发现你的套路全部都在意料之中,对方早就预判了你的预判。
这时候你的选择是什么?当然是原谅她,老老实实买个会员再说。
我是在一个偶然的机会看到知乎上 @临时哈桑 对思维导图的评价中知道 Freeplane 的,原话是 Freeplane 从性能到功能都吊打各种商业产品,唯一的缺点是丑。当时我刚好是想要替换到 XMind 的,除了贵,就是偶尔卡顿,Lock-in 成本较高,于是抱着很期待的心态下载了 Freeplane 进行使用。
先说优点吧,别的不提,Freeplane 是开源免费软件,这一点就足够让人心动了,大部分人的使用频次都没有那么高,为了一个一周用两三次的软件,每年花几百块,说实话是有点贵的。开源的优势在于,不用担心这个软件哪天突然死掉,比如 XMind 现在公司经营不善倒了,可能会良心的提供非会员也导出功能;还有一种可能性是趁倒之前捞一波,劝你赶紧买会员导出。而开源软件就不会有这个问题,大不了自己把代码拉下来编译一次就可以了,该怎么用就怎么用;况且一般这种开源软件不再维护的时候,通常会提供比较完整的善后手段,或者会有其他产品接盘。
其次,功能是真的齐全,我个人不负责任的猜测 XMind 中的大部分功能可能是有借鉴 Freeplane 的,上图可以看到,支持各种样式、总结、指向等等,甚至编辑框直接内嵌了一个 HTML,可以自由发挥。
支持 URL Scheme,可以从任意支持 URL 的软件直接跳转到对应的导图甚至节点,配合很多编辑软件或者 GTD 软件有奇效。
可以说作为一款免费软件,确实功能已经非常丰富了,单从功能上来说,可以碾压之前的 XMind8,部分功能超过现在的 XMind Zen,不过XMind Zen 实在发展太快,已经赶上来甚至部分超越了。
说完了优点,来聊一下这款软件的缺点。
首先, 界面是真的丑,软件是使用 Java 开发的,延续了十几年前那种 Java UI 的风格,可以说对比起 Mindnode 和 XMind Zen 来说,完全下不了手,不过这个也在预期内,可以理解,毕竟是开源产品,用爱发电。
其次,中文支持不够完善,我个人自从学会双拼之后就再也离不开了,可以说双拼极大地提升了我的打字效率,然而,Freeplane 对双拼支持贼差,每次新建节点编辑的时候,都会将双拼的第一个字母也带上输出出来,大概长这样: ,测试的双拼是 ceui
,就会把第一个 c 带到文字里面,十分不方便。
F2
进入文本编辑模式,之后再进行输入就 OK 了,不过每次都这么搞一下,依然十分蛋疼图片编辑很不方便,无法像 XMind 那种,好歹是把图片拷贝到他自己的资源库目录中,Freeplane 的图片只能选择本地文件进行插入,且只是挂了一个链接,大概长这样,可以看到格式是使用的 URI, 也就是标定一个本地路径,。
好处是什么呢?保存的导图文件里面不带有原始图片,大小会非常小,同样节点的 XMind 大约是 Freeplane 格式的 4 倍大小,算上大图片那就远远不止了;坏处也非常明显,当你本地这个图片的位置发生变更之后,对应的导图文件当然也没有图片了,直接报错,大概长这样:,你应该能想象出来,这是有多么蛋疼。
文件格式是纯文本,Freeplane 的文件格式采用的是 XML 格式,后缀名为 .mm
,是不是有点熟悉,没错,默认打开方式是 Xcode,让我有种回到之前开发 iOS 的感觉。短暂怀念一下之后你会发现,纯文本格式的劣势就是完全无法预览,我在文件列表里面看到一堆导图的时候,根本不知道每个里面都是什么,除非我一个个打开,会有种拆盲盒的感觉,这时候你才会发现 XMind 这种可以直接在文件列表里面预览全部内容的软件是多么的可爱。
如果说上面的问题都还是小毛病的话,那么导出问题可以说是非常致命了。看起来 Freeplane 支持的导出格式非常的多,不仅支持PDF、Markdown、XML、PNG,还支持 HTML、SVG,可以说很多格式我听都没听说过,比如 .tji
、 .twi
等等,总之就是非常强大啦。然而实际使用的时候你就会发现,导出 PDF 时候直接整个 App 卡死,这没什么,毕竟它有提示你,但当你等了一小时之后,依然卡在这里的时候,你大概也会像我一样怀疑人生了。不确定这个 bug 是否跟中文有关。
同时它还支持分支导出,嗯,跟整个导图导出支持的格式还不一样,真正常用的 PNG、PDF这时候你会发现全部不支持,OK,这个忍了。继续操作,导出 Markdown 总可以了吧,这时候你会发现,永远提示你导出失败,且重试无效,依然失败。
吐槽结束,总的来说,Freeplane 在某些地方确实还是有可取之处的,例如内链、支持总结、高度自由的编辑器等等,但作为一个用户来说,说实话我无法接受使用这种产品,如果你不是程序员,你可能接受程度比我还低,根本不知道为什么 .mm
后缀会无法预览,不明白什么是 XML,玩不懂里面的内链。我的结论是这款软件可以把玩一下,但不适合普通用户日常使用。
幕布(未完待续)
ProcessOn(未完待续)
这里上一个汇总表格,可以比较直观的看出各个软件的差异,目前看起来最能打的依然还是 XMind,希望后面开发者能够多上点心,用产品而不是用小手段吸引用户。
]]>五一期间阅读完毕了《小强升职记》一书,感觉收获颇丰,对自己的时间管理进行了更加深入的思考和了解。本文就针对我在阅读过程中所记录的一些笔记以及思考实践进行一个回顾总结。
这本书的主线是介绍主人公职场菜鸟小强在大牛老付的帮助下,一步步理清楚自己在时间管理方面所犯下的一些错误,并且进行改正完善,之后慢慢从菜鸟走向一名比较成熟合格的技术负责人的整个历程,按照这条成长路线来进行叙述的。可以看出作者在这方面的功底非常深厚,深入浅出的就把时间管理中常犯的一些错误和如何应对逐步的展开来讲述。
作者类比了一棵树苗成长的过程来进行章节划分:
我感觉这个类比稍稍有点不是很切合,全文比较干货的是在第二章,详细介绍了一些非常实用的时间管理工具,其中有不少是我之前就一直在实践的,但系统的跟着作者再次学习一遍,感觉自己之前只是了解了一些皮毛。这也给了我一个教训,自己随便网上搜了一下一些概念,然后就上手实践是有一些不太可取的地方的,还是需要通过书籍来深入了解一下背后的思想理念,相比于附带的知识价值来说,书籍真的是非常非常便宜的东西了。
接下来我简单按照阅读的顺序进行一些重点汇总。
开篇老付先是通过给小强介绍“时间黑洞”的概念来引导小强进行思考和记录,明白时间黑洞在我们日常工作生活中的占比有多高。尤其是当下我们这个信息洪流时代,可以说你的专注力无时无刻不在被周围各种信息所打断所消耗。打个比方说,你只是想要打开手机搜一下某个资料或者文章,解锁手机之后发现有人给你发微信消息,赶紧点进去看看内容,聊了一会之后,收到了新的公众号提醒,翻进去看了一下,又弹出微博或者抖音的推送消息,当你刷了半小时抖音之后,已经完全不记得之前想要查找什么了。可以说这已经成为一个很普遍的现象了,毕竟现在专门有着高薪的产品经理在研究如何做出一个紧紧抓牢你的产品,配合人工智能大数据等,例如抖音会给你疯狂推送你喜欢看的内容,几乎可以做到让你一下午坐在沙发里面刷抖音刷到水都想不起来喝一点。这也是我为什么越来越不喜欢在手机上进行深度阅读和思考的原因,不要太高估你的意志力和专注力,也不要低估你手机上那些 APP 对你的影响。
老付给出的解决方案是进行时间日志记录,这里有两种记录方式。首先,刚开始使用的时候,可以把自己每天的时间花费在哪些事情上面挨个记录下来,知道自己一天有多少时间是真正工作的时间,有多少是划水、玩手机等;之后,慢慢了解了自己的时间使用分布,可以先设定一下自己今天的时间安排,最好细化到每个小时,然后记录实际完成情况,中断次数等,目的是为了明确自己被打断的频率,自己的高效工作时间。
这里可以推荐几个我在实践过程中发现的比较切合这个场景的软件。
首先,Mac 上可以使用”Qbserve”软件,它可以很方便的记录你在使用电脑时候的效率情况,包括每个 APP 的使用时长、浏览的每个网站等,还是蛮细致的,但它有个很明显的缺点,无法记录手机时间,同时跨设备目前还不支持。手机上可以使用“时间块”这款软件搭配使用,用来标注自己每天的时间使用情况,不过它也有个明显的缺点,自动化程度太低,几乎全是要靠手动点击操作,尽管软件已经简化了使用步骤,但每天到了晚上提醒的时候,回想一天的事情,然后一个个点击,其实还是蛮消耗精力的。
Qbserve 截图:
时间块截图,可以看到每天实际认真工作的时间比你想象的要少得多:
其次,需要安排自己每天的工作,细化到下一个小时应该做什么,然后记录下实际完成的情况,包括被打断的情况,坚持一周之后你就可以知道自己的高效时间段在哪里。通常来说,一般人的精力是跟血糖浓度成正比的,所以为什么快到饭点的时候效率很低,就是因为血糖含量较低,所以上午 10-11 点,下午 2-4 点通常效率是比较高的。但是由于白天被打断的频率会比较高,也会有很多人喜欢在晚上甚至夜里工作,这样更容易专注,找到适合你自己的高效时间段是很有必要的,因人而异。比如我自己,最近发现早上效率还蛮高的,没有人打扰,所以我最近在坚持早起,每天 7 点半左右起床,离 9 点出门上班,中间有一个多的专注时间,可以用来进行看书、写作等,同时中午公司一般有午休习惯,我会提前去吃中饭,在吃过中饭之后快速进行一会午休,然后在大家还在休息的时候,可以找到大约半小时左右的专注时间,用来思考下午的工作,或者完成一些比较重要的工作。
找到高效时间的意义是什么呢?这里有一个原则叫做“三只青蛙”,就是说每天早上梳理出当天必须要完成的三件事情,通常是重要性或者困难度比较高的事情,优先把这几件事情完成,其余的零碎小事做不完也没有关系。所以找到你最高效的时间段,安排“三只青蛙”就显得比较重要了。
书中讲到一个例子十分发人深思:同事 A 每次重要的事情都能办的很棒,但是在一些零碎小事上经常出问题;同事 B 平时各种小事做的很好,但一旦遇到重要的大事,就出幺蛾子。如果你是老板,会如何对待这两位同事呢?答案很残酷,从老板的角度考虑,重要的事情办的很棒的同事 A 明显是可以委以重任的,可能还会给他安排一个秘书或者助手协助完成一些琐碎的小事;同事 B 一到关键时刻老是掉链子,那么就把各种琐碎小事都分配给他。这样的结果就是同事 A 逐步得到重用,且每天只需要完成一些重要的事情即可;同事 B 逐步被边缘化,永远只能在琐碎的小事中忙的不可开交。所以找到对老板来说比较重要的事情来做,以及锻炼自己完成要事的能力是非常重要的。
那么如何判断一件事情对你来说有多重要呢?随后作者给出了一个评判标准: 职业价值观。
所谓价值观是指一个人对周围的客观事物(包括人、事、物)的意义、重要性的总评价和总看法。
职业价值观指的就是对于你的职业生涯来说,你对平时遇到的这些客观事物的意义、重要性的评价和看法。听起来是很虚的东西,这里作者提供了一个如何找到职业价值观的方法,是一套职业价值观自测的题目,我在网上找到了对应的网站: http://www.181du.com/survey/Workvalues/index.aspx 。
一共 52 道题目,我自己做下来之后,得到的排名前三的价值观分别是:智力刺激、人际关系、社会地位。
不过思考下来,你就会发现这里作者讲的还是稍微有点不够透彻,价值观跟日常事情的评判中间感觉缺失了一个环节。首先,这个价值观题目你做完之后,会发现并没有特别深的感触,起码对我来说是这样,并不会有那种开悟的感觉,一下子就知道了自己的目标之类的,甚至你可能都觉得它不准;其次,有了这个价值观之后,你就知道如何评判一件事情的重要性和优先级了嘛?好像也并没有,实操性有点差。
我个人觉得,价值观可能只能作为一个指导思想,引导你进一步进行思考,实际上如果真的想要做到日常事情可评判,还需要一个更近期的目标和工具,为了解决这个问题,通常公司里面都会进行目标管理,一般使用 OKR 或者 KPI 这样的工具进行管理。对于个人来说,也可以设定一个未来 3-5 年的目标,然后逐步拆解,做一个年度计划,月度计划等,这样你就知道现在分配给你的事情对你来说优先级如何,接下来应该做什么。这个目标制定感觉后面讲到的六个高度更加贴近实际使用。
这一章我自己感觉是这本书最精华的部分,起码对于初步开始进行时间管理的我来说是这样,介绍了很多实用的管理工具和思想。我印象最深的就是:
做事要考系统而不是靠感觉
之前虽然也有积累一些自己的小系统,但是并没有这么明确的提出来,看到这句话的时候确实有点共鸣,有点类似于之前看过的《清单革命》一书,后面会注意把自己平时逐步在形成的小系统全部记录下来,真正的做到处理各种事情都有固定套路,这样才能既高效又不容易出错。
这篇里面很核心的一个内容就是介绍四象限法则,这个大家应该都已经听过了,所有的待办事项按照重要性和紧急程度可以划分为重要且紧急、重要不紧急、紧急不重要、不重要不紧急四类。
首先,我们要明确的是,待办事项的重要性和紧急性是如何判断的呢?原则如下:
其次,我们要知道如何应对这四个象限的任务:
如上图所示,这里的原则是:
下面我们详细展开一下:
这个没什么可说的,应该是去立马完成的。
我们工作中的主要压力就来自于第一象限,我们生活中的主要危机也来自于第一象限。
不过这里有个点需要思考的是,我们需要避免太多事情进入第一象限,原因也很简单,他会毫无原则的排挤开其他任何事情,独占你的时间,那么随着进入这个象限的次数越来越多,你会逐渐被事情推着走,变得毫无目的的不停地忙碌,这当然不是我们想要的。
那么应该如何减少进入这个象限的次数呢?应该说进入这个象限的任务大多数是由于第二象限没有处理好导致的。例如,下个月要进行一次汇报,正常情况下应该提前完成部分工作,例如整体大纲,部分材料收集等等,但由于人天然的惰性,不到 DDL 绝不动工,那么当你发现明天或者后天就要进行汇报的时候,这个事情就进入到了第一象限,导致你不得不连续熬夜完成它,同时效果必然会打折扣。
有计划的去完成。通常来说,这个象限的事情都是一些需要长期坚持,且短时间内很难看到明确收益,换句话说也就是延迟满足的事情,例如,阅读一本书、学习一门语言等等。
第二象限的优先级是最高的,因为它的长期收益最大,对你的成长最有帮助。但大多数情况下都被第一象限或者第三象限那些更紧急的事情阻塞导致没有进展,人总是会优先处理那些简单且有明确时间的事情,这样的好处就是可以得到及时反馈和满足,这个是人类的天性,正常是很难跳出这个坑的。
那么我们应该如何让自己尽可能的多处于第二象限呢?
消除时间管理的三大杀手——信息不够、拖延、预期结果不明确。
我们需要对收集篮里面的任务或者待办事情进行进一步的处理,针对上面提到的时间管理的几个坑:
委派给他人去完成。通常来说,这个象限经常会出现一些零碎的小事情,可能完成它对你来说并不会有太大成长,但是由于是老板指派或者是某个大项目中的一个不可或缺的环节又或者是他人的一些请求,导致你不得不停掉手上其他事情来完成它。这里有一个很有意思的“猴子法则”可以帮助你跳出这个象限。
比尔·翁肯(Bill Oncken)曾经提出一个理论,叫‘背上的猴子’。翁肯教授有一次偶然发现,自己在忙于加班的时候,下属竟然在优哉游哉地打高尔夫。这让他突然领悟到,主管人员之所以时间不够用,一个很重要的原因在于没有做好授权分责,将太多本该下属去做的工作招揽到了自己身上,以至于永远在苦苦追赶工作进度。
这里的猴子,指的就是待办的事情和职责。例如,中午休息的时候突然遇到了愁眉不展的同事,经过询问,原来是被某个技术难题所阻塞了,经过半小时的讨论发现确实是个棘手的问题,你一时半会也没有思路,这时候你说我回去再想想。之后这个猴子就从同事身上跑到了你的身上,然后同事就可能琢磨别的问题了,过半天过来问一下,上次那个问题有方案了吗?这时候你就会发现你的时间慢慢被这些原本不属于你的事情和职责所沾满了,导致你无法完成自己的第二象限甚至第一象限任务。
当然,帮助他人是有必要的,前提是你要先完成自己的事情,且要注意分寸,明确职责,避免让他人的事情成为你的事情,不要轻易的被他人“赋能”了。
下属问:我们怎么解决项目预算超支的问题?
你回答:你有什么想法?能不能先请你做一个删减成本的计划案?
不过这里有两个重点一定要明确:
那么如果有些时候,确实有一些老板指派的或者他人的事情不得不你来完成,应该如何处理呢?这里需要加上你自己的思考,了解清楚这个任务的上下文背景、明确具体的时间点、同时需要问清楚预期结果,也就是上面提到的“信息不够、拖延、预期结果不明确”。
这个没什么可说的, 尽量不做,尤其是尽量别在工作时间进入该象限。比如玩游戏、看电视剧、刷抖音什么的,偶尔忙碌空闲可以稍微放松一下,但是注意分寸,毕竟现在的手机软件太吸引注意力了,很容易就陷入“时间黑洞”中而不自知。
这篇主要是介绍了一下如何实践 GTD 工作法,作者形象的比作衣柜整理法,之前我对 GTD 也多少有些了解,但并没有知道的这么详细,下面详细记录一下。
GETTING THINGS DONE® is a personal productivity methodology that redefines how you approach your life and work.
GTD 是由 戴维·艾伦 在《搞定》一书中提出的方法论,这里是官方网站。它的目的是用来快速整理你的日常生活和工作,把他们从混乱变得井井有条。
GTD 包含 5 个步骤:
捕捉的意思是说,有任何想法、接到新的任务,不要中断当下的事情,而是应该把这些想法和任务记录下来,存放到收集篮(Inbox)中。
这里需要遵循的原则如下:
目的是为了分辨出待办事情中哪些是可以立刻行动的,哪些是需要进一步处理的。
这里并不是要实际执行行动,而只是单纯的确认这些事情要怎么处理,也就是判断重要性、识别急迫程度、明确优先级、是否可以指派给他人、是否可以在两分钟内完成等。
原则:
这里主要是按照上面已经明确意义的原则对待办事项进行整理,产出的内容:
定期的对自己当前的任务进行思考,时间点推荐在每天下班和每个周末完成。包括:
确保脑袋里面只装一件事情,这里可以利用 番茄工作法,使用一些例如降噪耳机、白噪声等工具辅助自己提升专注力。
同时,通过把任务拆分的尽可能细,就可以在每完成一小步的时候就进行打勾消除,这是一种很有成就感的事情,这种正向反馈会激励你进一步完成后面的步骤,不知不觉中就把一个大任务完成掉了。
这里有一些技巧可以用于提高行动的效率,主要是在快速记录待办清单的时候,对任务描述的足够清晰,因为在记录事情的时候你是很清楚要做什么的,但等真正开始做可能已经是几天后了,这时候还要拼命回忆之前的对话和上下文,比较费力。所以这里的技巧主要是用于消除不确定性:
作者对上面的步骤汇总整理了一幅流程图,感觉很好的表达了 GTD 的主要步骤,可以时不时的拿出来翻阅一下:
这部分主要是讲了两块内容,如何解决拖延症和如何使用番茄工作法。
关于如何解决拖延症这里基本思路类似于养成一个好习惯,在后面详细展开一下,这里略过。
番茄工作法(英语:Pomodoro Technique)是一种**时间管理法**,在1980年代由Francesco Cirillo创立。
番茄工作法是一个易上手难精通的方法,说起来其实很简单,
番茄工作法的原理是人的精力是会随着持续工作不断下降的,所以我们需要通过不断的短暂休息恢复部分精力,确保接下来能够高效工作,示意图如下:
通常来说,这里最难得就是中间的 25 分钟专注工作,一般作为一个社畜,时刻要注意关注钉钉消息、被人当面找到咨询问题等等,很容易就会中断自己的番茄。如何才能最大限度的专注工作呢?
其实上面行动技巧里面已经基本都提到了,除了通过一些仪式或者建造更好更安静的工作环境以外,就是需要对 GTD 的不断实践,每次遇到中断事件的时候,仔细问一下自己,是否可以忙完手上现在的工作再去评估;最好就是直接先放入收集篮即可。
更具体的内容可以参考一下《番茄工作法图解》这本书。
这部分内容主要是介绍如何养成一个好习惯。我们都知道习惯的力量是很强大的,可以这么说,我们日常生活中有很大一部分行为是完全由习惯控制的。例如你每天早上起来第一件事就是先掏出手机看一下时间,然后刷一会朋友圈,不完全清醒的情况下已经完成了刷牙和洗脸;即使已经换了工位,刚开始几天还是会经常走错,等等。
同时我们经常会在新的一年给自己立一些“flag”,例如最多的就是,每天坚持跑步、每天坚持读书、坚持早睡早起、学习英语等等。但结果我们也都知道了,大多数人办完健身卡就等同于已经健身完了,之后再也没见过教练。
这里作者给出的解决方案是这么几个原则:
我再补充一下我自己在实际实践过程当中的一些经验:
更具体的内容可以参考一下《游戏改变世界》这本书。
这章主要是介绍如何将你的灵感和想法逐步落到实处,对我自己来说,收获最大的是其中提到的“六个高度”。
David Allen提出我们的工作和人生是可以划分成六个高度去进行检视和规划的。
具体如下:
这一部分也可以参考 《ORK 工作法》,使用 OKR 来制定你的个人目标。
同时这里针对每一个阶段,也有不同的工具可以用来帮助我们梳理目标。
在梳理个人目标的时候,SMART 原则十分有用,通常是公司里用来制定 KPI 的时候需要用到,他代表的意思是 Specific—目标明确、Measurable—目标可衡量、Attainable—目标可实现、Relevant—目标与其他目标具有相关性、Time-based—目标必须要有截止时间。
明确自己的角色以及平衡各种角色的时候,九宫格就派上用场了,你制定的目标中,不仅要包含自己的工作计划,还需要考虑过程中其他角色的精力分配,例如如何维持人际关系、如何保持身体健康、如何提升财富水平等等。
接下来的任务和项目维度,可以分别使用思维导图和甘特图来进行梳理。
思维导图是很适合用来理清楚事情脉的,相比起文字版的待办清单,人对图像的接受程度和记忆力明显更高,因为清单本身是一个列表形式,而导图可以直接看到全貌。同时,也有助于跟他人进行分享演示。这里导图中还有很多分支,例如鱼骨图、时间线等等,适用于更加特定的细分场景。
甘特图是项目管理的利器,通常来说,项目管理管理的就是资源,包括时间、人力等,而甘特图可以很好地进行人力分配,看到全局进度,明确每个人的任务情况。可以很方便的进行周报汇总、风险预警等。
至此,这篇文章的主要核心点基本已经梳理完毕,不管是从篇幅上来说,还是重要程度上来说,我觉得核心主要在于第二章 GTD 相关部分。
接下来,我简单分享一下我自己在时间管理方面所形成的小系统以及用到的一些工具。
这部分内容其实在上面已经提到了,我目前用下来比较方便的软件分别是
同时,记录任务完成情况的话,可以通过语雀做一个 Excel 模板,每天进行更新。不过这部分主要是用来明确你自己平时的工作效率是否高效,同时明确你自己的高效工作时间段,我个人觉得靠手工录入时间块或者记录完成情况的方式,几乎是不可持续的,毕竟人都是有惰性的,简单执行一两周达到目标即可。不过类似 Qbserve 这种,如果有类似安卓端的工具,可以直接跟电脑打通,自动记录全部的时间使用信息,还是蛮不错的。
基于上面提到的灵感捕捉原则,在挑选这方面软件的时候,就必须要满足以下条件:
我选择的软件分别是 Cubox 以及 滴答清单,他们各自有着不同的使用场景。
针对公众号或者 RSS 获取到的信息,如果想要临时记录下来后续抽时间处理,可以使用 Cubox,它最大的优点就是能够直接在微信公众号的浏览过程中通过发送到助手直接进行保存,而不用拷贝链接切换软件保存或者使用捷径保存,打断次数越少越好。
而滴答清单,不得不说真的是个良心神器,我已经是三四年的老用户了,它的优势包括:
更详细的关于 PKM 中信息获取以及整理可以参考之前的一篇文章: 万字长文—关于PKM收集与整理系统的思考和实践
这里可以按照作者的方式,将待办事项分为项目清单、将来清单、行动清单。我个人在实际实践过程中,新补充了一些自行添加的清单:
我的滴答清单使用完整流程大概是这样:
同时这里,配合项目清单和任务清单的拆解,还需要使用思维导图工具和甘特图工具,这里我选择使用的软件分别是 Xmind 和 Omniplan。XMind 是一款国产的跨平台思维导图软件,目前使用下来最符合我的需求,后面会单独写一篇测评;Omniplan就不说了,Omni 系列属于知名高效工作组件了,目前出的产品几乎各个精品,OmniFocus 也是一款非常棒的 GTD 软件,我之前也使用过一段时间,不过由于功能太强大也就是太复杂,最后还是转投滴答清单了。
我目前深思的方式包括每日日记、每周计划及回顾、年度计划等,这里除了 Markdown 以外,强烈推荐 Textexpander 这款软件,这是一款快速输入文字的软件,内置了很多有用的函数,例如当前日期、时间戳,还可以进行不同 snippets 的组合,我几乎每天都在使用。
这样每次当你输入 ;dd
的时候,都会自动展开为今日日记,节约了大量的输入工作。类似的软件还有很多,例如Alfred 也支持快速输入文字,不过时间格式方面自由度太低,其他倒还不错。
这里我建议还是入手一个降噪耳机,能够大幅度提升幸福感,避免外界嘈杂的声音干扰你。搭配白噪声软件效果更佳。
同时这里不得不再次提一下良心的滴答清单,不过支持番茄钟的功能,甚至还支持了白噪声。
界面大概长这样,使用起来十分方便,而且还支持统计功能,可以自行设置每个番茄的时间,连续 4 个番茄之后的间隔休息时间等等,可以说是非常贴心了。
至此,关于我个人在阅读这本书的心得和笔记,包括我的一些实际实践都已经介绍完了,如果大家也对时间管理感兴趣,欢迎来一起留言讨论~
]]>PKM闭环中有一个很重要的环节就是信息输入,包括各种信息来源,例如微信公众号、博客、知乎、RSS等等,因此也就诞生了一大堆稍后读软件,如何真正有效的获取输入而不是做一只仓鼠是需要思考的。最近看了《小强升职记》很有收获,里面提到最好在每个方面都有一个自己的小系统,“做事要靠系统而不是靠感觉”,因此这里简单记录下自己的思考和目前构建起来的信息收集整理系统。
手机在浏览阅读一些比较有价值的文章的时候,无法立马吸收,例如读到一些专业的技术博客、长文等等,此时,就需要将这些内容收藏起来有时间的时候继续深入阅读。
手机的使用场景决定了它本身只能进行一些文章价值的快速筛选,辨别出这篇文章是否需要进一步深入学习,仅此而已。
所以我给自己定的一个小目标就是每天的手机使用时间不要超过3小时,微信、知乎中发现的有价值的内容都第一时间收藏到稍后读中,电脑上统一进行整理,手机只作为一个收集器和过滤器,用于快速识别出哪些文章需要进一步研究。
关于这点,收到了简悦作者的部分观点启发。
个人观点:不要有太多碎片化时间,利用碎片化阅读本身就是个伪命题,原因上面也有讲到。
很多人恨不得自己24小时都在进行阅读,可能是为了造成一种自己很努力错觉,也有可能是因为现在这个时代知识焦虑实在被贩卖的太严重,如果不让自己时刻在学习在阅读就会被淘汰?实际上这样做并不会对个人提升有太大帮助。很多人(包括我)都会利用走路、吃饭时间进行手机阅读,这种只能用来看看新闻,我理解是无法进行自我提升的。
那有些人说就是会有很多碎片化时间应该如何处理呢?比如通勤时间较长,或者一些工作性质导致会有很多碎片化时间。
我最近的感受下来,这种小块时间宁可放空,进行思考和回顾。拿我自己来说,之前每次上下班开车的时候总是喜欢听一点书、或者听一些专业相关的教程,例如喜马拉雅、极客时间、得到等,实际上听完一遍,根本没有吸收,最多只能做到,知道有这么本书,大概讲的是个什么东西,但具体到如何讲的一概不知。最近这段时间尝试放空自己,开车就好好开车,什么也不听,发现经常会有各种灵感从脑袋里面冒出来,比如对工作的回顾、对自己日常的思考等等(同时也会让我更加专注开车,更加安全)。大家也可以尝试一下,定期放空回顾自己说不定会让你走的更快更远,PS:这里也可以学习一下如何进行冥想,我自己也在尝试。
知道了需求背景,我们就开始真正开始进行软件挑选了,经过我这么多年的软件使用实践,我发现大多数时候其实我们并不知道自己需要什么软件,外加现在这个时代,开发一个软件成本实在有点低,一旦有个热点,同类型产品几乎是雨后春笋般冒出来(参考最近大火的Roam类双向链接笔记)。如果不建立一个自己的筛选甄别系统,很容易就被各种工具迷花了眼。
这里我简单列举一下我自己这些年总结的一些选择软件的原则,其中很多在我的旧文中都有提到,仅供大家参考:
对于我来说稍后读软件方面个人的诉求点包括:
搞明白为什么需要一个信息收集系统跟它需要具备哪些能力之后,我们就可以开始进行一些工具的筛选和鉴别了。目前笔记软件和稍后读软件实在是太多了,很容易就挑花了眼,我这里只简单介绍一下我自己深度使用过的一些软件和工具,当然我自己在使用过程中也并不是一下子就想清楚需要哪些能力再开始进行挑选的,也是逐步使用过程中发现哪些适合自己哪些不适合自己,因此现在把使用过程中的一些经验简单说一下,希望能减少大家的时间成本,同时由于每个人情况不同,需求也不一样,这方面主观因素比较强,最好是梳理出你自己的需求点,建立最适合你的小系统。
这里我把目前用过的工具简单分为两类,一类是笔记类,一类是收藏类,他们的定位有所区别。笔记类,偏向剪藏、管理、检索、写作;收集类,偏向快速收集、标注、阅读。
老牌仓鼠专用笔记,只收藏不老牌仓鼠专用笔记,只收藏不处理。我从11年左右就开始在用印象笔记,当时还没有国内版,同步速度比较一般,不过真的是稳,最强大的就是剪藏功能,能够完整保存网页内容,选区可以调整,不过一般来说自动识别完全够用;其次是搜索功能,甚至能利用OCR,搜索图片内容。
优点:
缺点:
大概在15、16年左右,由于实在受不了印象笔记对于Markdown的支持力度,同时不愿意进行太多付费,转而开始使用为知笔记,但不得不说,使用体验比起印象笔记简直是天上地下,服务响应不及时、开发进度缓慢、客户端经常卡死等问题一点点再消耗我的耐心,终于还是舍弃了它。
编写本文的时候我还专门去下载了为知笔记,发现网上能够搜索到的资料基本都是16、17年的了,为知笔记的官网中,Linux跟Mac客户端在20年之后更是完全没有更新过,不得不说还是挺可惜的, 其实它本来很有机会超越印象笔记的。
微软出品,必属精品。大部分情况下这句话没啥毛病,很多人对Onenote十分狂热,我简单试用过一段时间,实在有点不太习惯,放弃了。
优点:
缺点:
待补充,这个软件已经好几年没用过了,最新情况已经不太清楚
在iOS跟Mac上发家的一款新兴笔记软件,最近也开启了双链功能支持,我简单使用过。现在已经改名叫做熊掌记了,国内化做的不错。
优点:
缺点:
不想补充了
对于大多数场景,这类软件才是真正能够用于收藏的,笔记类软件能够当收藏使用说实话是被印象笔记一手带起来的风气。
国外老牌稍后读软件,与Pocket齐名,以排版优雅、界面简洁著称
优点:
缺点:
同上,国外老牌稍后读软件,感觉相比Instapaper对国内的优化更好一点,但社交元素有点多,我只想安安静静的阅读个文章,不想有这么多干扰
优点:
缺点:
国外的一款比较有名的网页收集类软件,收费理念比较特殊,随着使用人数增加,费用逐步提高,我知道的比较早,但购买的比较晚,当时购买的时候已经到了一年40刀,说实话我觉得有点太贵了。好在支持退款,用了几天之后毫不犹豫的退款了,不是很适合我。
优点
缺点
国产类似 Pinboard 的软件,做的很蛮不错,且针对国人习惯加了很多新功能,例如支持直接保存文字、图片等。价格也比较公道,但移动端体验实在有点糟糕。产品定位是一个纯网页保存类软件,基本类似于浏览器的收藏夹, 不过支持标签、检索等,对稍后读的支持力度其实一般,或者说没有,不支持阅读模式、标注等。
优点:
缺点:
国外一款书签收集软件,之前有段时间蛮喜欢的,界面比较清爽,后来因为网络因素改为使用收趣云了。
优点
缺点
国产软件,基于 Linnk 重构升级而来,可以说是目前用下来最符合快速收藏文章诉求的软件了。跟作者有过简短的几次邮件对话,交流下来比较省心,回复很快,也很能接受用户意见,促使我毫不犹豫的购买会员。 会员价格一年78¥,可以说很良心了。
优点:
缺点:
国产软件里面很优秀的GTD工具,甚至个人觉得没有之一。用来收集灵感十分方便,配合桌面小插件,基本可以做到手机上3s内记录想法。同时各平台支持都很到位, 价格定位也很良心,平时轻度使用完全可以不购买会员。
以下是会员专有功能
优点:
缺点:
起先是靠做网页上面的阅读模式起家,支持油猴插件,作者对于深度阅读有比较深入的思考,有过对话,对我挺有启发的。最近2.0版本之后,迭代速度飞速提高,更是紧跟潮流,在双链笔记结合上花了大量心思,包括各种标注导出、文章导出、稍后读上都较之前版本有很大提升,重点是,大版本买断只需要18¥,真的非常划算了,建议大家入手一个。
优点:
缺点:
国产稍后读软件,大概16年左右还是火了一阵子的,毕竟国内软件大多针对国人使用习惯做过优化,再加上网络同步速度远快于国外软件。但缺点也很明显,一般Lock-in都比较高,想办法让你从其他各种软件导入进来,导入方式支持的很到位;但想要导出?没门,各种加门槛,要么要求付费,要么干脆不支持。后来由于找不到太好的赢利点,且会员收费策略做的不好,现在已经很少有人用了。
优点:
缺点:
已经升级成为Cubox,不再赘述。
经过以上各个软件的简要分析,相信大家也看出来我的一些偏向了。目前个人的PKM流程大概如下,算是个人PKM2.0版本(PKM1.0版本,有关2.0版本输出部分后面有机会再介绍一下):
如果是外部输入,例如通过微信公众号、知乎等发现的一些有价值的文章,快速通过 Cubox 保存,不要在手机上花费太多时间。
如果是日常的一些灵感和任务相关,随手记在滴答清单的收集箱中。
单纯的收集是没有意义的,不能满足于当一只仓鼠,在整个知识内化过程中,有一个很重要的步骤就是组织整理,这个跟GTD的理念也是相同的。我会设定一个闹钟,例如每天晚上或者每周末抽出一定的时间来清空收集箱。
对于Cubox中收藏的文章,花费1-2分钟快速明确是否跟当下的关注点和Areas重叠(这里涉及到PARA工作法,有机会再单独整理一篇),如果是需要近期关注的,那么进入简悦稍后读或者直接开始进行阅读,完成阅读后,把笔记备注和标注导出为素材笔记(卡片盒笔记中的概念,也可以理解为PARA工作法中的Resources),存入Logseq中
对于滴答清单中的待办项,根据四象限法则进行排序整理,如果不是当前需要立马完成的事情,纳入到将来清单中, 定期回顾即可(这里可以参考小强升职记中的概念)。
在这个环节中,双向链接笔记是一个很重要的工具,我对它的定位是一个信息输入输出的枢纽。所有的灵感、外部输入都在简单筛选之后进入到素材笔记中进行打标签、分类、链接,同时需要输出某个特定主题的时候,可以通过双向链接快速查找到相关的笔记内容,整理归纳到博客或者其他外部输出中。
最近这一两年,双向链接笔记已经火的不行了,但目前真正国内稳定可用的软件屈指可数,目前我个人在使用的是Logseq这款,关于双向链接笔记的使用选择方面,完全可以单独再整理一篇了,先挖坑,后面一定补上 :)
将信息快速内化为知识的一个有效手段就是传授给他人,这其中输出博客是一个很好的方案。现在搭建维护个人博客的成本极低,同时又有很多类似于飞书、语雀这样的平台用来进行输出,唯一限制你的可能就只有你的懒惰了(说的就是我自己)。
我目前采用的方案是Hexo,这方面可以参考 如何使用 Hexo 搭建个人博客 ,这里不再赘述,值得一提的一款软件是MWeb,这是一款博客写作发布软件,目前支持很多平台,包括语雀、少数派、Wordpress 等等,值得一提的是,它还支持使用 Metablog API,这样就可以同时在多个博客平台进行发布。
关于我个人的PKM实践,大体方案就介绍到这里,希望对大家有所帮助,关于PKM的更多信息可以参考我的旧文,虽然很多工具软件已经过时或者需要更新了,但整体的思路是没有大变的。这里的一个很重要的原则就是不要太过热衷于折腾工具,都是小术而已,只有真正了解了道,才能做到各种工具都能随心所欲的驾驭,最终的目的是为了学习知识,而不是学习工具。
]]>2021-05-08: 1.0版本初步完成,待完善已完成任务回顾
2021-05-10: 1.1版本完成,修改不重要不紧急为将来清单,且新增每周回顾
最近在阅读《小强升职记》,感觉里面讲到的时间管理工具和方法十分有用,自己之前虽然也了解一些工具,例如四象限原则、两分钟方法等,但看完这本书之后感觉之前的理解还是太肤浅了。是时候升级一下自己的时间管理系统了。
在此之前我的工具大致包括: 稍后读软件、GTD工具(滴答清单)、写作工具(一般是VSCode+Hexo)。最近半年沉迷于使用双链笔记软件,目前在用的是 Logseq,后面专门展开介绍一下。目前在尝试将GTD工具、笔记都整合到 Logseq 里面,主要是受了这篇文章的启发 OKR + GTD + Note => Logseq,下面就详细介绍一下我的实践过程。
首先,需要简单学习一下Datalog语法。Logseq是基于Clojure开发的,它的底层可以简单地理解为是类似于数据库的结构,因此可以天然的支持各种查询,这也是我们能够利用它来实现 GTD 管理的前提。因此这里如果需要掌握如何使用 Logseq 来进行查询的话,最好先学习一下如何使用 Datalog。可以参考 官方的查询教程 以及这篇 Learn Datalog Today,粗略扫一遍,能够看懂大概意思就可以了。
其次,Logseq本身是列表大纲形式,无法直接支持四象限这种样式,因此需要一些 CSS功底,这里我是直接参考了 OKR + GTD + Note => Logseq 里面的样式,原始版本在 [css+template] eisenhower matrix,可以简单看下。
《小强升职记》里面提到日常待办事项是分为项目、任务、行动三种的,只有行动才是可以立马去执行的,项目和任务都需要进行分解,拆成行动。
结合Logseq,我目前考虑使用属性来解决,也可以考虑使用标签,不过我认为会对内容有一定的污染,各有利弊吧。
针对一个新的行动或者任务,添加属性type字段,然后根据具体的类型添加行动、任务、项目字段。
样例:
注意:所有的待办都必须要有明确的优先级和截止日期,这么做有几个原因:
1. 要求自己对每个待办都设置明确的时间点,避免添加到收集箱中一直不处理
2. 添加了优先级之后才好进行四象限统计
3. 目前还没有找到Logseq的query条件中如何同时查找DDL在限定时间内以及无DDL限制的任务,只能查找其中一种case,这个后续解决
实际使用过程中,可以把一下的查询条件汇总到一个模板中,
1 | ## #.v-eisenhower-matrix (用于CSS选择器,最好不要改) |
具体查询语句如下:可以根据自己的实际使用需求将属性限制移除或者修改时间限制,代码中均已经添加注释。
2021-05-10 补充: 将设置为Later的事项移动到第四象限,这样就可以定期回顾;同时新增一个每周回顾的query语句,由于目前Logseq还不支持查询当前时间戳,因此用了个笨办法,每次查询的时候手动输入当前时间戳ms进行查询。
1 | #+BEGIN_QUERY |
1 | #+BEGIN_QUERY |
1 | #+BEGIN_QUERY |
1 | #+BEGIN_QUERY |
1 | #+BEGIN_QUERY |
这里直接照抄了 OKR + GTD + Note => Logseq, 不过它的CSS中内容较多,我单独提取了四象限相关的部分,放在 四象限CSS 中,查询模板也都放在这里,便于大家使用。
目前我的每日笔记大概长这样:
1.1 版本已经解决
可以算是1.0 版本,目前存在的问题包括:
Logseq是个很棒的工具,期待尽快稳定下来,后面长期使用,作为笔记+GTD一站式平台~
]]>术语:
TODO
wekan 是一款开源的看板系统,从功能上来讲模仿看板类软件鼻祖 Trello。整体界面大致如下:
采用 wekan 的原因也比较简单, 为了安全性考虑,公司内部文档禁止使用部署在外部服务器上的工具。
官方教程:https://github.com/wekan/wekan/wiki/Install-Wekan-Docker-in-production
docker-compose up -d
或者 docker-compose up
目前是在 Mac 服务器上搭建了 wekan,暂时没有域名,大家先使用 IP 地址登录, http://30.3.131.128,之后就是注册账号: –统一约定使用中文花名,邮箱使用公司邮箱–,
之后进入看板首页,这里可以管理个人名下所有项目,新增看板也在这里,下面以工程组为例,简单介绍下如何使用看板。
首先,创建一个看板,
接下来可以看到看板的整体页面,
接下来需要做的事情就是建立列表和泳道,这里我们约定,需求跟任务划分为两个看板,例如,所有工程618的需求放到一个看板中,包括待定需求、进行中的需求、已完成需求,不需要写明需求详细工期,仅列出本期都有哪些需求即可;所有的需求细分出来的任务单独放一个看板中,需要细化到每人每天的工作。需求看板示例:
这里我们约定划分这么几个列表:
右侧面板中需要添加其他成员,这样其他人才能访问
之后我们就需要根据刚才整理出来的需求进一步分配工时,列表可以参考下图:
主要分为描述、成员、标签、耗时、评论等。
按照需求优先级划分标签
Personal knowledge management (PKM) is a process of collecting information that a person uses to gather, classify, store, search, retrieve and share knowledge in their daily activities (Grundspenkis 2007) and the way in which these processes support work activities (Wright 2005). It is a response to the idea that knowledge workers need to be responsible for their own growth and learning (Smedley 2009).
在当下这么一个信息爆炸的时代,我们所缺少的不是如何收集知识,而是如何主动获取知识和辨别整理。我个人通常的信息来源有这些:Google(是的没错)、RSS 订阅、微信公众号。
这些信息大部分是通过手机获取的,也可以利用碎片时间阅读,例如等公交、排队等等(不建议吃饭或者上厕所时候看,会影响身体监控)。核心功能诉求如下:
这里有几个过滤筛选信息的原则:
这里推荐几款我自己在使用的软件:
今天开始会写一系列 Java 后端学习的笔记,一方面是为了以后翻阅查看,更主要的原因是通过写作输出的方式让自己的印象更深,避免遗忘。
首先是简单记录下自己学习使用 JDBC 的历程,由于目前基本都是通过一些类似 MyBatis 的框架来进行数据库操作,所以 JDBC 的使用不需要掌握太深入,仅作为了解即可。
首先我们学习任何东西之前都需要先了解几个问题,基本上的思路是:
1. xxx 是什么?
2. 有什么作用?也就是为什么需要 xxx?
3. 怎么使用(简单入门即可)?
4. 分别就主要链路进行知识补充
之后,可以根据实际情况决定是否要进一步深入了解,还是只作为简单学习即可。
JDBC 也不例外。
Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标[1]。JDBC是面向关系型数据库的。
上面是维基百科的解释,从这里我们获取到的几个有用信息:
回答类似这种问题其实套路也比较简单,我们思考一下,假如没有 JDBC 会怎么样?
我们知道,关系型数据库是有很多分类的,例如常见的 MySQL、Oracle、SQL Server等等,那么如果没有一套标准接口的话,意味着我们如果开发 MySQL 数据库,不光需要引入 MySQL 的官方驱动,还需要引入官方的 Jar 包,根据对应的 CRUD 接口编写数据库操作代码,如果这时候你还有另一个使用 Oracle 项目,那么同样的工作需要重新进行一次,可想而知,这个适配成本是随着数据库厂商的增多逐渐上升的。
那么怎么解决这类问题呢?有一句名言是这么说的,“计算机科学领域的任何问题都可以通过增加一个简介的中间层来解决。”,回到我们的问题上,Java 开发者就提供了这么一个中间层,也就是 JDBC。
通过这个图可以看到,由于有了这么一个中间层,我们在开发代码时,只需要使用 JDBC 相关的接口即可,不需要关心具体实现底层的是 MySQL 还是 Oracle。
接下来我们看下如何快速搭建一个 JDBC 的demo。通常在 Java 后端开发中,我们只需要掌握几个核心点就可以快速上手一个框架,它们分别是:
下面我们就从这个几方面入手快速了解一下:
使用数据库需要这么几个比较重要的包:
java.sql
:所有与 JDBC 访问数据库相关的接口和类,可以说有了这里面提供的接口和类已经足够搭建一个 demo 实现基本功能javax.sql
:数据库扩展包,提供数据库额外的功能,如:连接池。接下来我们只是实现基本功能,可以不使用这个包。这里面只有数据库的驱动是需要我们自己使用 jar 包或者 maven 依赖的。
1 | <dependency> |
这里由于我本地的 MySQL 是 8.x 版本,因此使用了 8.0.17, 如果你的 MySQL 是 5.x 版本可以使用 5.1.39。
JDBC 中可以通过使用配置文件来加载数据库相关配置,例如指定数据库 URL、账户、密码等,这个等下详细介绍。
JDBC 中几个比较核心的类和作用如下:
简单了解即可,等下具体搭建工程会详细介绍。
这里我们先简单建一个表,然后插入几条数据用于查看。
1 | CREATE TABLE `bank` ( |
这样我们的表就建好了,数据也有了,我们就正式开始通过编写代码查找表中的数据。
其实说白了使用 JDBC 的核心步骤就是这些:
那么我们看下,这里其中导入 jar 包我们已经通过引入 maven 坐标解决了,接下来看看如何注册驱动。
1 | Class.forName("com.mysql.cj.jdbc.Driver"); |
这不就是加载类吗?哪里有注册驱动呢?别着急,我们看下源码。跳到 com.mysql.cj.jdbc.Driver
中,我们发现 Driver
中的静态代码块调用了 DriverManager
的registerDriver
方法,然后创建了一个Driver
对象作为入参传入。这也跟我们之前提到的 DriverManager
有管理和注册驱动的作用是吻合的。
1 | static { |
我们看下 DriverManager
的官方文档,可以看到
JDBC 4.0 Drivers must include the file META-INF/services/java.sql.Driver. This file contains the name of the JDBC drivers implementation of java.sql.Driver.
Applications no longer need to explicitly load JDBC drivers using Class.forName(). Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification.
这几句话是什么意思呢?简单来说就是可能 JDK 觉得大家老是这么加载驱动比较繁琐,在 JDBC4.0 之后新增了一个约定,数据库驱动实现方必须要在 jar 包的 META-INF/services/java.sql.Driver
路径里面写明指定的 Driver
类,这样 JVM 会自动将对应的驱动进行加载,而不再需要程序员在开发过程中手动调用 Class.forName()
了,当然已经调用了的代码可以正常运行而不会受到影响,所以这一步我们可以省略了。
接下来是获取数据库连接对象 Connection
,前面我们已经提到,这个类是用来创建 Statement
对象的,而 Statement
对象就是真正进行数据库操作的类,
1 | final Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/samwei12", "root","root"); |
通过调用 DriverManager
的getConnection
方法,我们就得到了一个 Connection
对象。它的几个入参也比较显而易见,就是数据库连接时的 url、账户、密码等信息。这也是 DriverManager
的第二个功能,就是获取数据库连接对象 Connection
的实例。
接下来是定义 sql 语句,这个最好理解,这里我们把张三的存款设置成 1000。
1 | String sql = "update bank set money=1000 where name='张三'"; |
接下来是创建 Statement
对象,
1 | final Statement statement = connection.createStatement(); |
执行 sql 语句:
1 | final int count = statement.executeUpdate(sql); |
入参代表我们需要执行的 sql 语句,出参代表本条 update 语句影响的行数。
1 | System.out.println(count); |
这里我们简单打印下就可以,如果查询到的是需要封装的对象,那么还需要其他处理,这里不展开。
最后就是释放资源,这个一定要记住。
1 | statement.close(); |
之后我们执行一下,可以看到打印结果是 1,同时数据库中对应数据也发生了变动。
示例代码地址:
前面已经讲得比较清楚,它的主要作用就是注册驱动+管理数据库连接,这里只说一下 MySQL 的 URL 写法。
jdbc:mysql://ip地址(域名):端口号/数据库名称
jdbc:mysql://localhost:3306/samwei12
jdbc:mysql:///数据库名称
数据库连接对象,它的主要功能包括创建一个 Statement
和管理事务。
Statement createStatement()
PreparedStatement prepareStatement(String sql)
setAutoCommit(boolean autoCommit)
:调用该方法设置参数为false,即开启事务commit()
rollback()
执行sql的对象,CRUD 都可以执行:
boolean execute(String sql)
:可以执行任意的sql语句int executeUpdate(String sql)
:执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句ResultSet executeQuery(String sql)
:执行DQL(select) 语句这里我们最常用的的一般就是最后一个方法了,示例代码:
1 | //4. 定义sql |
通过 ResultSet
的 next
方法遍历结果,之后得到的就是一行的数据,分别取出每一列的数据处理即可。
结果集对象,封装查询结果
boolean next()
: 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回truegetXxx(参数)
:获取数据int getInt() , String getString()
executeQuery
这里已经展示过了执行sql的对象,继承自 Statement
。看到这里你可能会比较奇怪,我们不是已经有了 Statement
类了嘛,为什么还需要再搞一个子类出来呢?我们先通过一个示例介绍一下什么是 SQL 注入。
创建一个用户登录信息表,包括用户名和密码。
1 | create table user ( |
然后模拟一下用户的登录,如果用户输入的用户名和密码能够匹配到一条记录,那么我们认为登录成功,否则认为登录失败。
代码比较简单,核心代码如下:
1 | String sql = "select * from user where name='"+userName+"' and password='"+password+"'"; |
验证一下:
1 | 请输入用户名: |
1 | 请输入用户名: |
看起来好像一切正常,没毛病,那么我们尝试一下如下输入:
1 | 请输入用户名: |
这时我们发现,神奇的事情出现了:
1 | 登录成功,欢迎您:我是一个不存在的用户 |
这是为什么呢???其实我们 debug 一下就会发现,原因很简单,我们传给 Statement
对象的是一个字符串,那么上述参数真正执行的 SQL 语句是 select * from user where name='我是一个不存在的用户' and password='我是密码' or 'a'='a'
。 大家发现了吗?我们的原始 SQL 只是查询用户名和密码信息,但是如果用户输入了类似上面这种奇怪的密码,在我们的 SQL 后面拼接上其他字符串,就可以绕过我们的检查,随意输入任何用户名和密码都可以获得登录授权,这无疑是很危险的。
SQL注入(英语:SQL injection),也称SQL注入或SQL注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。
以上是维基百科对应的词条解释,那么怎么解决这种问题呢?JDK 的作者们提供的 PreparedStatement
就是用来处理这种问题的。我们来看一下它的用法。
PreparedStatement prepareStatement(String sql)
: 指定预编译的 SQL 语句,SQL 语句中使用占位符 ?
创建一个语句对象int executeUpdate()
: 执行 DML,增删改的操作,返回影响的行数。ResultSet executeQuery()
: 执行 DQL,查询的操作,返回结果集使用步骤:
"SELECT * FROM user WHERE name=? AND password=?";
PreparedStatement
对象setXxx(占位符的位置, 真实的值)
具体的代码示例如下:
1 | // 1. 构造一个带参数的 SQL 语句,这里不需要加单引号了,而是使用占位符 ? |
使用起来还是比较简单的,需要注意的几个点已经写在了注释里面。
执行结果如下:
1 | 请输入用户名: |
可以看到使用 PreparedStatement
有效的解决了 SQL 注入的问题,它还有以下好处:
prepareStatement()
方法会先将 SQL 语句发送给数据库预编译。PreparedStatement
会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement
对象并执行。减少 SQL 编译次数,提高效率。所以我们尽可能全部都使用 PreparedStatement
的方式,而不是 Statement
类。
至此,一个简单的 JDBC demo 就搭建完毕了。由于目前已经不会有人直接使用 JDBC 进行数据库操作了,我们只是为了接下来学习 MyBatis 进行一个铺垫,简单了解即可,不需要深入。
示例代码地址:
虽然做iOS开发的过程中使用过 **Cocoapods**, 但是对里面的细节了解其实不算太多,直到这两年做织女项目时,通过对Cocoapods进行Qt支持改造才开始深入了解部分细节,这个过程中,网上没有找到太多相关资料,本文就简单介绍下我对Cocoapods提供的插件机制的一个简单了解,希望能给大家带来一些帮助。
在此之前,我们简单看下 Ruby Open Classes
,这部分是为未接触过Ruby的同学准备的,熟悉的同学可以直接略过。
在Ruby中,类永远是开放的,你总是可以将新的方法加入到已有的类中,除了你自己的代码中,还可以用在标准库和内置类中,这个特性被称为Ruby Open Classes
。下面我们通过一个示例简单看下。
首先,我们自定义一个类Human
,放在human.rb
文件中:
1 | class Human |
接着,我们新增一个main.rb
:
1 | require_relative 'human' |
之后,我们在main.rb
中重新定义hungry
方法,
1 | class Human |
可以看到,这里在我们新增hungry
方法之后,所有的Human
类的实例均调用我们的新实现了,即使是已经创建好的实例,这里故意放到两个文件中是想说明这个特性是可以跨文件甚至跨模块的,对Ruby内置方法的替换也是可以的(谨慎使用)
1 | puts "hello".size |
这个特性是十分强大的,让我们可以很容易的对三方模块进行扩展,也是Cocoapods
的插件体系所依赖的基础。
Cocoapods
的插件体系整体流程还是比较清晰的,下面我们就来逐步看下。
首先,Cocoapods
提供了一个便捷的命令行工具库 CLAide,这个库包含很多功能,例如,一套命令基类,一套插件加载机制等。
Command基类在lib/claide/command.rb
中,这里提供了大量基础功能,包括 run
、 options
、 help
等等。
首先,当我们每次执行 pod xxx
命令时候,会执行 bin
目录下的可执行文件pod
。
1 | require 'cocoapods' |
这里实际上是 Pod
模块从CLAide
继承了子类Command < CLAide::Command
,我们执行Pod命令时候,就会调用
1 | def self.run(argv) |
实际上只是扩展了一些检测git版本、xcode证书等,真正核心部分还是调用的CLAide
的实现:
1 | def self.run(argv = []) |
可以看到这里真正执行命令之前会遍历所有的插件前缀,并进行插件加载,回过头来再查看 cocoapods/command.rb
会发现,这里指定了约定的插件前缀
1 | self.plugin_prefixes = %w(claide cocoapods) |
可以看到这里的插件分为两种,我们目前只关心文件名为cocoapods
前缀的插件。
我们深入PluginManager
的具体实现看下,
1 | def self.load_plugins(plugin_prefix) |
为了减小篇幅,这里只贴了核心相关代码,整体的流程大致是:
PluginManager.load_plugins
并传入插件前缀PluginManager.plugin_gems_for_prefix
对插件名进行处理,取出我们需要加载的文件,例如cocoapods
前缀在这里会转换为所有包含cocoapods_plugin.rb
的gem spec 信息及文件信息,例如~/cocoapods-qt/lib/cocoapods_plugin.rb
PluginManager.safe_activate_and_require
进行对应的 gem spec 检验并对每个文件进行加载至此,基本的插件加载流程大致梳理清楚了。
下面我们看下如何自己扩展一个插件,关于这部分,Cocoapods其实也基本已经帮我们做了很多事情了,主要是 cocoapods-plugins, 它提供了一个插件创建的完整生命周期,包括新增、发布、检索等。
执行 pod plugins create cocoapods-test
之后,发现自动帮我们创建了一个gem工程,其中的 lib 文件夹下果然存在了一个 cocoapods_plugin.rb
文件,整体的目录结构如下:
1 | ├── Gemfile |
这里最核心的就是cocoapods_plugin.rb
,我们前面分析过,执行pod命令时候会主动加载所有cocoapods_plugin.rb
文件,那么只要我们将需要扩展的类加到这里面,执行命令时候就会生效。
1 | class Test < Command |
可以看到这里其实只是新增了一个 Test
命令,并加了一些描述信息。为了让我们的扩展能生效,我们可以通过几种方式,
为了更贴近实际生产发布流程,这里我们采用第二种方式。
首先,我们编译生成gem产物,
1 | gem build cocoapods-test.gemspec |
其次,本地安装
1 | gem install ~/CocoapodsQt/cocoapods-test/cocoapods-test-0.0.1.gem --local |
此时,我们再执行 pod 命令
可以看到我们扩展的命令已经生效,接下来就可以开始愉快的coding了。
至此,我们对Cocoapods的整体插件流程应该有了一个比较清晰的认识了,希望能给大家带来一些帮助。
]]>cocoapods-plugins
模块,这个按理讲是安装 Cocoapods 时自带的,可以使用命令 gem install cocoapods-plugins
进行安装pod plugins create NAME
1 | # 这里是对外暴露的文件 |
lib/cocoapods_plugin
文件中加载你 hook 的文件即可cocoapods-plugins
,也可以放到自己公司的内部 Gem 源中_mm_unpacklo_ps
Selects and interleaves the lower two SP FP values from a and b.
将两个128位浮点数的低64位分别取出,返回新的128位浮点数
1 | INTERLEAVE_DWORDS(src1[127:0], src2[127:0]){ |
输入如果为:
1 | a0,a1,a2,a3 |
则输出结果为:
1 | a0,b0,a1,b1 |
_mm_mul_ps
1 | FOR j := 0 to 3 |
分别将两个128位浮点数的每个float相乘,结果仍然是128位浮点数
也就是:
1 | dst[31:0] := a[31:0] * b[31:0] |
_mm_moveldup_ps
1 | dst[31:0] := a[31:0] |
Source data: 0.100000 0.200000 0.300000 0.400000
Calling _mm_moveldup_ps to load the values.
Result: 0.100000 0.100000 0.300000 0.300000
将128bit浮点数的第一个及第三个float分别复制到第二个及第四个float中
参考链接:
https://zh.mweb.im/mweb-1.4-add-floder-octpress-support.html
https://zhuanlan.zhihu.com/p/30513914
网上搜索到相关资料,如上面的链接,发现最新版本 MWeb (3.1.2)步骤有所出入,添加外部文件夹时候并没有弹出设置页面,需要自己手动在左侧文件夹上右击,点击编辑,
]]>最近在项目开发中,需要针对 Jenkins 项目进行配置,Jenkins 的 job 配置采用的是 xml,在维护配置模板的过程中就遇到了问题,因为逐步发现配置灵活性超出了字符串的范畴,本文旨在简单介绍 Python 下模板引擎模块 Jinja2 的使用。
Jinja2 是一个 Python 的功能齐全的模板引擎。它有完整的 unicode 支持,一个可选的集成沙箱执行环境,被广泛使用,以 BSD 许可证授权。
以上是官方说明,简单来说,它提供了替换功能(变量替换)和一些强大的特性(控制流、继承等),可以快速生成数据文件,使得业务与数据分离开来,满足一些灵活多变的配置需求。
拿 Jenkins 配置来说,如果要实现不同平台、不同项目的配置,有以下几种方案:
起初,我选用的是方案一,特点是简单粗暴,不需要额外的配置,例如,两个项目的 git 仓库配置可以采用如下代码实现:
1 | # 略去不相关代码 |
这样,只需要外部传入地址及分支,即可获得不同的项目配置 xml,但缺点也很明显如果两个配置节点数目不相同或者里面的字段差别很大,那么就需要使用 template2
、template3
、template4
等等,因此此方案仅适合配置需求较为简单,字段不是很复杂的场景。
紧接着,开始调研快速生成不同 xml 数据的方案,找到了 https://stackoverflow.com/questions/1055108/fast-and-easy-way-to-template-xml-files-in-python, 这里提到了两种方案,也就是上述的2、3。
方案2的特点也很明显,可以完整控制 xml 中每个字段和属性,灵活性确实够了,能够满足我们的需求,但是有点大炮打蚊子的感觉,而且频繁的读写 xml 也比较消耗性能,业务逻辑也会随着配置的灵活性和多样性急遽膨胀,比如 Jenkins 项目配置,仓库地址、分支信息、ssh 信息、tag 字段、打包脚本等等,如果使用 xml 解析的话,针对这些节点都必须创建对应的业务逻辑,不是很推荐。
那么我们的选择也比较简单了,Jinja 可以快速的替换生成我们业务需要的各种各样的数据,而且接入成本低,业务逻辑不会变化太大,下面简单介绍下本次项目中的使用。
这个比较简单,看下文档即可,执行命令
pip install jinja2
Jinja2中有一个名为Environment
的对象,用于存储配置、全局变量,并且用于从文件系统中加载模板。即使你通过Template
用字符串创建一个模板,Jinja2 也会自动为你创建一个环境。推荐使用这种方式进行模板配置而不是使用字符串,大多数程序使用一个环境即可。
1 | from jinja2 import Environment, PackageLoader, select_autoescape |
加载一个模板:
1 | self.__job_template = self.__env.get_template('job_template.xml') |
接下来就是传递一些业务参数了,
1 | self.__config = self.__job_template.render( |
以上都还是比较简单的 API 调用,核心使用部分在与如何在模板中通过 Jinja 支持的语法灵活的进行数据配置。
最基础的功能就是进行变量替换了,语法为{{变量名}}
, 例如将 url 传递到 xml 中,模板中写法为(省略了不相关内容):
1 | <userRemoteConfigs> |
然后在代码中向上面一样调用 template.render(git_url='xxx')
即可
Jinja 支持基本的if
、for
、宏等语法,这也是它最强大的地方,目前项目中我只用到了判断就够了。用法如下:
1 | {% if push_tag %} |
上面这段 xml 的逻辑就是如果传入了 push_tag
变量且为True
则在 Jenkins 配置中增加推送 git tag 节点,否则不做任何处理。
除此之外,Jinja2 还支持循环,
1 | <dl> |
详细说明可以参考 http://docs.jinkan.org/docs/jinja2/templates.html#for
这部分由于目前项目配置还比较简单,暂时没用到,可以参考 http://docs.jinkan.org/docs/jinja2/templates.html#id23
对于简单的项目来说,上面提到的一些基本替换加控制流基本都能满足使用了, 稍微复杂一点的程序可以考虑使用继承和扩展。
总的来说,Jinja2 既强大又简便,完美符合项目中一些HTML、XML 的业务配置需求,目前项目中本身有的三四个 xml 文件用了 Jinja2 之后缩减成一个,同时改动工作量大约只花了几个小时,真的很方便。
]]>参考资料:
我们都知道ARC中weak
与assign
或者说unsafe_unretained
最大的不同就是设置weak
属性后,系统会在对象被释放后自动将指向对象的指针置为nil
,而assign
则会产生一个悬空指针,那么系统是如何实现这一机制呢?我们能否自己模拟系统对weak
的实现呢?
通过查看runtime源码中objc-accessors.h
和objc-weak.h
部分,我们大概可以了解系统针对weak
的实现方式与strong
或者copy
的实现方式是不同的。
对于注册为weak
的对象,系统会以weak
指向的对象内存地址作为key,将之放入到一个hash表之中,当此对象的引用计数为0时会调用dealloc,之后遍历hash表中此key所对应的对象,将之置为nil,关于这部分详细内容可以自行查看源代码,在此不进行扩展。我们这里重点介绍下如何自行模拟系统这一机制。
首先我们回想一下weak
机制的几个步骤,其中真正与assign
不相同的部分在于当对象调用dealloc
之后对对象指针置空操作。那么我们很容易联想到Objective-C中关联对象的使用,关联对象也是在对象调用dealloc
之后被移除,这样我们是否可以通过对对象添加一个关联对象来模拟weak
的实现呢?
下面我们通过代码来说明下如何自行实现一个类似weak
机制的对象管理机制,本文中所用到的代码存放在https://github.com/samwei12/WeakDemo :
unsafe_unretained
和weak
具体使用中的差异首先,创建一个ClassA
类,
创建一个ClassB
类,并在里面添加一个测试方法print
,用于等下向ClassB
实例对象发送消息,确认对象是否仍在使用
1 | - (void)print { |
给ClassA
添加一个ClassB
对象属性,并设置为unsafe_unretained
1 | @property (nonatomic, unsafe_unretained) ClassB *objectB; |
在main函数中添加如下代码:
1 | @autoreleasepool { |
运行一下代码,不出意外的话,程序会挂掉,因为instanceA.objectB
所指向的内存已经在instanceB = nil;
时候被释放掉,instanceA.objectB
仅指向一个悬空指针:
1 | 2016-03-09 15:25:10.427 WeakDemo[98402:1613532] Object is 0x7fa080d0f100 |
当然,也有可能不会挂,这取决于执行print
函数时,instanceB
所在的内存是否完全释放掉。
如果将ClassA
中的属性改为@property (nonatomic, weak) ClassB *objectB;
则不会出现crash,这就是weak
的作用了,objectB
对象如果被释放掉,则该指针变为nil
,而向nil
发送消息是不会出现问题的。
unsafe_unretained
添加weak
功能下面我们就使用unsafe_unretained
来一步一步模拟weak
修饰符:
创建一个DeallocBlock
类,代码如下:
1 | typedef void (^voidBlock)(); |
覆盖它的dealloc
函数,这样就实现了该对象被释放时执行外部传入的block
功能。
添加一个NSObject
的category,如下:
1 |
|
这里主要实现的功能是,给所有的NSObject
对象添加一个传入block
的方法,这个方法的作用就是在其中根据传入的block
生成一个DeallocBlock
实例,并使用关联对象将这个实例关联到自身。之后当这个对象被释放时候,会自动移除所有的关联对象,也就会触发DeallocBlock
实例的dealloc
方法,执行我们传入的block
。
接下来,我们重载ClassA
的setObjectB
方法:
1 | - (void)setObjectB:(ClassB *)objectB { |
这段代码主要作用就是给_objectB
添加一个deallocBlock
,这样,如果_objectB
所指向的内存已经被释放掉,则会调用我们传递进去的block
来将此悬空指针置为空
再次运行程序:
1 | 2016-03-09 15:53:36.881 WeakDemo[99270:1639867] Object is 0x7fad79c0f310 |
由此可知,在main
函数中执行instanceB = nil;
之后,开始执行deallocBlock
,之后由于instanceA.objectB
已经为nil
,print
方法不执行操作。
至此,我们已经实现了自己模拟系统weak
机制的运行,给一个本会产生悬空指针的unsafe_unretained
修饰符加上了weak
的功能。大家可以自行尝试https://github.com/samwei12/WeakDemo,如果有任何问题,欢迎指正。