iPad 上新闻类软件的字体选用

上次在 twitter 上和 Typeisbeautiful 的 Rex 提到这件事情,最近刚收到 iPad,就来分析一下。

iPad 上的新闻类软件非常多,多到 app store 上有个 News 分类专门给这类软件,但良莠不齐也是自然的,但从字体选用看,我认为做得最有诚意的还是三个传统媒体:New York TimesNYT Editors’ Choice, WSJThe Wall Street JournalFinancial TimesFinancial Times iPad Edition。它们共同的特点是,为了优化阅读体验,都使用了专门订制的字体,随 app 一起提供,几乎完美地保持了纸面版本的字体风格。

Continue reading “iPad 上新闻类软件的字体选用”

阅读器的进度显示与估计


对电子书阅读器来说,提示当前阅读进度是一项很自然的功能,习惯用电脑的人都常看到进度条 (progress bar) 和滚动条 (scroll bar),如右图是 [Textus](http://www.jjgod.org/projects/textus) 中使用的右侧滚动条。

然而在实际实现中,进度计算是一件伤脑筋的事情,比如 Textus 的实现其实很简单:在打开文件时读入整个文件,然后整个交给 [Core Text](http://en.wikipedia.org/wiki/Core_Text) 去排版,将排版后的结果分解为行 (`CTLineRef`) 记录下来,并将所有行总的高度设置为整个文本视图的高度,这样,每当滚动视图 (`NSScrollView`) 移动到某个位置时,重绘函数 (`-drawRect:`) 被调用到,我们根据该位置来判断应该绘制从第几行到第几行的内容,再调用 Core Text 把这些行画出来。

这么做看似很简单直接,结果也很容易保证正确,带来的问题是,每次用户修改设置 (比如调整字体大小、窗口尺寸) 时,就得把整个文件重新排版一遍,即使此时我们只需要看到**当前一页**的内容。为什么这种方法这么低效,我还一直使用它呢?因为这个实现严格依赖滚动视图给出的位置来判断当前阅读进度,所以总的高度估计必须非常精确,不然随便滚动一下就可能出现错位,而一次算给出整个高度的方法最准确,不容易出错。

Continue reading “阅读器的进度显示与估计”

Mac OS X 下与 SSD 相关的优化

最近刚入手一块 Intel X25-M G2 80G SSD,使用效果不错,于是整理了下面这些跟 SSD 有关的优化经验:

#### 安装

因为 SSD 比较小,而有些数据又不是那么需要快速的访问,所以我保留了 MacBook Pro 里自带的 320G 7200 RPM 硬盘,做了如下改动:

1. 买了一个光驱位硬盘托架,最有名,也是最贵的一种要 $99,但山寨版可以在淘宝找到,对于 Unibody 的机器来说,找 9.5mm 高的那种型号就行,比如 Fenvi 的,建议买 100 以内的。

2. 把 MBP 拆开,SuperDrive 取下,把原来的硬盘取出,放入光驱位硬盘托架,把 SSD 接在原来的硬盘所接的 SATA 口,然后把硬盘托架安装回原来光驱的位置。这种搭配是为了保证用 SSD 做系统盘 (挂载在 `/` 上) 时系统能正确的让硬盘休眠。当然,如果你比较奢侈地买两块 SSD,也可以让他们组成 RAID0,这样性能就更夸张了。

3. 给在 SSD 所在卷分区,只需要分一个区,安装系统。新的系统会把原来那块硬盘也列出来。这时修改 `/etc/fstab`,加入:

/dev/disk1s2 /Users/jjgod/Downloads hfs rw

这个做法是把原来那块硬盘挂载在我自己这个用户的 `Downloads` 目录,这样所有下载的内容都会放在这块比较大的硬盘上 (当然有用的我以后会转移到 SSD 上),考虑到 Downloads 主要起的就是一个临时的缓冲作用,这样还是比较方便的。然后重启机器,就能发现这块硬盘只被挂载在上述目录下了。

4. 除了下载内容,我还在这个目录放以下内容:

* VMware Fusion 的虚拟机文件,这是从别的机器复制过来的,直接放在这个卷的 `/Virtual Machines` 目录下,然后在 VMware Fusion 里打开就行了。需要注意的是以后 VMware 里创建新虚拟机的时候也得记住选择目录,别放在自己的 `$HOME/Documents` 下面了。

* iTunes 媒体库,这个可以在 iTunes 的 Preferences -> Advanced -> iTunes Media folder location 选择。我是先在这里选好了,然后从原来的机器里导入以前的 iTunes 数据。

* 其他下载软件的默认下载位置,包括 uTorrent, Transmission 和 aMule。

* [Steam](http://store.steampowered.com/) 的游戏目录,这个可以通过创建符号链接到 `$HOME/Documents/Steam Content` 实现。

#### 优化

上述安装过程后,系统已经能保证数据各自放在比较合适的地方了,参考 [Mac OS X SSD Tweaks](http://blogs.nullvision.com/?p=275) 这篇文章的介绍,我们还可以做如下优化:

1. 用 `noatime` 方式挂载系统盘,这样可以减少不必要的 I/O 次数,虽然 SSD 做这些操作非常快速,但考虑到**最后访问时间**这个属性其实很少用到,大家关心的一般都是最后修改时间和创建时间,所以完全可以关闭这个属性,这在 Unix/Linux 下是非常常见的文件系统优化选项。一个简单的方法是,修改 `/etc/fstab`,加入:

/dev/disk0s2 / hfs rw,noatime

重启后,系统盘的挂载就带上 `noatime` 选项了:

$ mount | grep ” / ”
/dev/disk0s2 on / (hfs, local, journaled, noatime)

2. 禁用冬眠 (hibernate) 模式以节省空间。在 Mac 耗尽电池时,会进入“冬眠”模式,将内存中的所有内容写入磁盘,下次唤醒后从这些内容恢复状态。所以系统会在 `/var/vm` 维护一个和内存等大的 `sleepimage` 文件,考虑到 SSD 空间宝贵,而一般绝少会遇到耗尽电池的情况,可以禁用掉这个功能以节省空间:

$ sudo pmset -a hibernatemode 0
$ sudo rm /var/vm/sleepimage

3. 减少临时文件的读写。

* RAMDisk 是常见的性能优化手段,对于内存充足的机器,把频繁读写的内容放到一个用内存为存储的虚拟磁盘中,能大大加快速度。虽然 Macintosh Performance Guide [最近的一个研究](http://macperformanceguide.com/OptimizingPhotoshop-RAMDisk.html)表明 RAMDisk 并不能很大地提升如 Photoshop 这类软件操作的性能,至少投入产出比不是很经济,但至少用它来保存一些本来就可以随意丢弃的数据是个很好的思路。[Mac OS X SSD Tweaks](http://blogs.nullvision.com/?p=275) 提供了创建 RAMDisk 的脚本,他的做法是在系统启动时创建一个 256M 的 RAMDisk,挂载在 `/private/tmp` 上面。他还建议把 `~/Library/Caches` 也放到 RAMDisk 里。最大的问题是,如果你长期不重启 (我一般的重启周期是 80 ~ 100 天),那有的临时文件,比如 `~/Library/Caches/com.apple.Safari/Webpage Previews/` 会增长到数百 M 甚至上 G。如果被它占满了 RAMDisk 的空间,那其他缓存数据就写不进去了。

* 考虑到 RAMDisk 是个比较有风险的优化手段,也可以用以下方法禁用掉 Safari 的 Webpage Previews 以减少临时文件读写。如果你像我一样从来不用 Safari 的“Top Sites”功能,一定会很讨厌它凭空占用大量的空间。

$ defaults write com.apple.Safari DebugSnapshotsUpdatePolicy -int 2

* 关闭 Spotlight 索引也是一个有用的优化手段,如果你像我这样从来不用 Spotlight 的话。

#### 结语

* [Macintosh Performance Guide](http://macperformanceguide.com/) 还提供了一些更奢侈的优化手段,这里不一一介绍了,因为我自己也没试过,有兴趣可以自己尝试。值得尝试的是它提供的 DiskTester 工具有一个 [recondition](http://macperformanceguide.com/Software-DiskTester-UserManual-recondition.html) 功能,通过大量写空白块来优化长时间使用后的 SSD 性能。

* “安装”部分感谢[草莓数码](http://www.berrydigi.com)的 iBook 和 MacBook 的帮助。

* “优化”部分大部分思路来自 [Mac OS X SSD Tweaks](http://blogs.nullvision.com/?p=275)。

是否该用 Core Data?

Core Data 是 Cocoa 里面一套非常受欢迎的框架,从 Mac OS X 10.4 提供以来,在 10.5 中引入了完善的 schema 迁移机制,再到 iPhone OS 3.0 时被引入 Cocoa Touch,这套完善的框架都被认为是管理大量结构化数据所首选的 Cocoa 框架,尤其是因为使用 Core Data 能大大减少需要手工编写的代码量,就使它更受开发者欢迎了。

不过最近却出现了一些不同的声音,先是传出消息说 Aperture 3.0 抛弃了 Core Data,改为直接操作 SQLite 数据库 (大家联想到 Apple Mail 3.0 也是直接用 SQLite,没有用 Core Data),但因为都是 Apple 内部的决定,大家只能凭空猜测理由;接下来,NetNewsWire 的开发者 Brent Simmons 也说在 NetNewsWire for iPhone从 Core Data 转向用 FMDB 来操作 SQLite 数据库 (FMDBGus Mueller 编写的一层很薄的 SQLite 在 Objective-C 下的封装),Brent 给的理由就很充分了:他要做的很多操作都是对数据批量进行的,其实不需要把所有数据都保存在内存里遍历执行,那样更慢,直接交给数据库,往往一条语句就搞定了,简洁而且快速。

然后 Jonathan ‘Wolf’ Rentzsch对此深表赞同,并推荐 Aaron Hillegass 的 <a href="http://weblog.bignerdranch doctissimo viagra en ligne.com/?p=232″>BNRPersistence 框架,这个框架用 Tokyo Cabinet 提供了一个类似 Core Data 接口的数据持久化方案,最大的优点是比 Core Data 快得多,根据 Aaron 自己的测试,常见的操作都要快 10 ~ 20 倍。其实快这么多也可以理解,毕竟 BNRPersistence 要比 Core Data 轻量得多,支持的功能也少很多,加上 Tokyo Cabinet 这样的 Key-value 数据库在处理适合它的操作时,多数要比 SQLite 这样的关系型数据库要快。

所以突然 Core Data 就有点被墙倒众人推的意思,好像以前大家都知道它不好用,但都不好意思说,直到突然有经验足够丰富的开发者开头,就一涌而上开始骂了。我个人的观感是 Core Data 作为官方方案,给开发提供的许多便利还是不可小视的,但考虑学起来确实也不容易 (所以才有人专门写本书讲 Core Data),所以新上手的 Cocoa 程序员不妨先考虑一下 BNRPersistence, FMDB 这样的方案。

那是否 Core Data 就该被抛弃呢?目前的争议其实有点像 Web 开发里到底该不该用 ORM,区别是大多数 Web 开发者因为历史原因,对直接进行数据库操作有偏好 (或者说,本能地反感 ORM),而多数 Cocoa 开发者则坚定支持用对象操作数据,所以长远来看,数据持久化方案在 Cocoa (Touch) 开发里少不了,唯一的疑问是 Apple 会不会继续改进 Core Data 的性能和接口,以拉近与第三方方案的差距,只要 Apple 还在不断改进,Core Data 就有学习的必要。

我个人对 Core Data 的怨念主要是在要写的“胶水代码”上:有的时候为了方便与界面的 bindings 操作,不得不给 Model 写大量重复的胶水代码,所以如果第三方的方案如果在这方面有简化,我很乐意改用。

用 XBMC 整理电影收藏

随着硬盘上收集的电影多了起来,怎么管理它们就成了问题,我之前采用的方法很简单:先把电影下载到内置的硬盘上,然后把看过之后觉得值得保留的那些,或者还没来得及看,但内置硬盘空间不够的时候,转移到外置硬盘 (一般是大容量的仓库盘) 上。一般这样的仓库盘就直接在根目录下分个 Movies 目录,然后所有的电影每个占一个目录,全部按它们原来的名字移动到这个 Movies 下面。类似这样:

$ cd /Volumes/Foo/Movies
$ ls
Apocalypto.2006.720p.BluRay.x264-ESiR
Gake.no.ue.no.Ponyo.2008.BluRay.720p.DTSES.3Audio.x264-CHD
Lock.Stock.and.Two.Smoking.Barrels.1998.720p.BluRay.x264-CYBERMEN
Mission.Impossible.1996.720p.BluRay.DD.x264-ESiR
Surrogates.2009.720p.BluRay.x264.DTS-WiKi
...

而自从 XBMC 被移植到 Mac 上以来,就开始关注这个软件内置的多媒体管理功能,毕竟大部分多媒体文件不是从 iTunes Store 上买的,所以 FrontRow 并不那么合用,而这个软件从功能全面和界面美观的程度上看都合适。不过之前一直都在用 Plex 这个 fork 版本,因为 Plex 确实对 Mac 相关的一些细节处理的比较好点,但也只把它当作一个单纯的媒体播放器来用,并没有用到它的库管理功能。主要是因为 XBMC / Plex 要求的命名方式是“标题 (年份)”这样的类 imdb 方式,而我又想保留原有的目录名 (方便找字幕什么的),所以一直没有实施。

更新:发现 XBMC 还能根据目录下放置的 nfo 文件中的 imdb 地址来查找,而且这样更精确,所以假如你每个目录都保存了 nfo 文件,可以不必修改目录的命名。

最近想到的取巧做法是,对每个这样的电影目录,都在本地目录建立一个符合 XBMC 命名规范符号链接,然后让 XBMC 对这个本地目录进行索引。于是写了这么一个脚本 试了一下,果然很方便,效果如图:

这里简单介绍一下步骤,供有兴趣的朋友参考:

  1. 推荐使用最新的 XBMC 9.11 版本,Plex 最新的 0.8.5 版本这方面支持得不好;

  2. 图中使用的是 Rapier 这个 skin,需要单独下载,推荐使用;(下载后将解压得到的目录移动到 ~/Library/Application Support/XBMC/skin 目录下,然后在 XBMC 的 Settings -> Skin 中选择 Rapier)

  3. 下载前面提到的这个 makelib.py 脚本,使用方法如下:

    $ chmod +x makelib.py
    $ ./makelib.py /Volumes/Megatron/Movies ~/Movies
    Apocalypto.2006.720p generic viagra 100mg.BluRay.x264-ESiR -> '/Users/jjgod/Movies/Apocalypto (2006)'
    Gake.no.ue.no.Ponyo.2008.BluRay.720p.DTSES.3Audio.x264-CHD -> '/Users/jjgod/Movies/Gake no ue no Ponyo (2008)'
    Mission.Impossible.1996.720p.BluRay.DD.x264-ESiR -> '/Users/jjgod/Movies/Mission Impossible (1996)'
    Surrogates.2009.720p.BluRay.x264.DTS-WiKi -> '/Users/jjgod/Movies/Surrogates (2009)'
    The.Cove.2009.LIMITED.DVDRip.XviD-AMIABLE -> '/Users/jjgod/Movies/The Cove (2009)'
    ...
    

    这么做的效果是扫描 /Volumes/Megatron/Movies 目录下所有匹配的目录,在自己的主目录下的 Movies 目录中建立对应的符号链接。

    需要注意的是,这个脚本会要求所有内容都是目录,而且命名都是 Movie.Title.Year.*.*.*.-Group 的格式,中间一段可以任意,但必须包含年份和小组后缀,如果你手头存的目录名称不规范,必须先修改为这种格式,有的来源目录名称就不包含年份,这时可以根据 themoviedb.org 或者 imdb.com 提供的年份来自行添加。如果要求不那么规范,可以修改脚本来忽略小组。另外还要注意的是,Director’s Cut, DC, Unrated, Limited 这样的词必须放到年份后面,要么自己改改脚本,过滤掉它们也行。

  4. 打开 XBMC,进入 Videos 部分,选择 Add Source,用 Browse 选择这个 ~/Movies 目录,在 Set Content 中,选择用 themoviedb.org 作为内容来源,选上 Use folder names for lookup,如图:

  5. 然后 XBMC 就会开始索引所有创建好符号链接的目录,如果能在 themoviedb.org 上找到对应记录,就会自动下载。这个时候你切换到 XBMC 的 Movies 部分,就能看到如前面截图一样的效果了。选择其中任何一个,按回车就会开始播放。按 c 键或者用 Apple Remote 的 menu 按钮则可以打开上下文菜单,这里可以编辑选定项目的信息、删除特定项目 (如果导入的内容不对)、设置已经看过的标志等等。注意在 XBMC 扫描的时候不要断开存储实际内容的硬盘,否则 XBMC 会认为本地存的是无效的链接而直接跳过。如果出现个别电影名称不规范导致找不到对应记录的,可以在更名后按 c 选择重新扫描。

  6. 如果你下载了新的内容,可以再次以同样的参数运行前面提到的脚本,这个脚本会跳过已经添加的内容。另外,还可以在 XBMC 的 Settings -> Videos -> Library 部分选上 Update Library on Startup,这样每次启动 XBMC 的时候就会自动扫描你添加为库的目录,检查是否有新的内容了。