0%

前⾔

本⽂旨在介绍⽂字渲染需要了解的基本知识点,以及些许延伸扩展;不涉及到实际代码的编写,不涉及具体算法实现,但中间穿插引⽤的外部⽂档有部分代码实现可供参考。

本文引用的图都是来源网络博文,部分图片并未记录来源。如涉及侵权请联系我。

字体发展历史

  • 在 Macintosh 电脑中,使用点阵字体(格式后缀:bdf、pcf、fnt、hbf等)。点阵字体放大后会出现锯齿、模糊的情况。
  • 在同一时期,Adobe 发明了基于 PostScript Type 1 的矢量字体格式,但 Type 1 字体是加密的,Adobe 通过售卖字体认证赚取利润,苹果也不得不向 Adobe 购买 Type 1 字体认证。
  • 于是,苹果决定设计全新的字体格式,最后在 1991 年发布为 TrueType 格式,同时也包括 Times Roman、Helvetica、Courier 等大量字体。
  • 但是 TrueType 字体反响并不好,因为大部分用户已经购买了 Adobe Type 1 字体,没必要再切换。因此,苹果联合微软对抗 Adobe,授权给微软使用 TrueType 字体。1991年,微软在 Windows 3.1 系统上支持了 TrueType,并和 MonoType 公司联合开发了大量著名字体,例如 Arial 等字体。
  • 1994 年,微软独自开发了 TrueType Open 字体;1996 年 Adobe 加入开发,兼容了 Type1 字体格式,更名为 OpenType。2007 年,OpenType 被国际标准组织 (ISO)采用。OpenType 常见后缀 otf、ttf、ttc。
  • 2023年,Adobe 将停止支持 Type 1 字体创作。

SFNT:SFNT 是苹果在开发 TrueType 字体格式时设计的通⽤字体数据存储结构,SFNT 是 spline font 或者 scalable font 的缩写,TrueType、OpenType、WOFF 等格式都采⽤ SFNT 作为容器。

WOFF:Web Open Font Format,采⽤压缩格式,字体⽂件更⼩,适合⽹⻚使⽤。可以简单理解为 OpenType + 压缩。⼀般⽐ ttf 字体⼩ 40%。WOFF2 是 WOFF 的下⼀代标准,在 WOFF 的基础上提⾼ 30% 的 压缩率。

阅读全文 »

Objective-C 是一门动态语言。所谓动态,在于消息发送和转发,在于 Method Swizzling,同时也在于 Non-fragile ivar。之前有一篇文章简单介绍过消息发送和转发(iOS教程(二)消息发送),这一篇主要介绍下 Non-fragile ivar 特性及其实现方式。

什么是 ivar ?

ivar(instance variable)就是类成员变量。一般情况下,property 都会自动生成一个成员变量。

假设有一个 MyObject 类,有 array 和 color 两个成员变量, MyObject 实例的内存结构如下:
ivar-layout

如果要获取 color 的值,self 指针加上偏移量,再解引用就可以了,这也是 runtime 获取成员变量的方式。

1
UIColor *color = *(UIColor *)((char *)self + 16);

当然,runtime 的逻辑比这复杂,例如 atomic 加锁等。

什么是 Non-fragile ivar ?

fragile 的含义是脆弱的,Non-fragile ivar 解决的是 fragile ivar 的问题。

fragile ivar 问题

在 32 位的 Mac 上,假定有一个 NSCustomView 继承自 NSView,NSCustomView 有两个成员变量。(至于为什么是 32 位的 Mac,接下来会介绍。)

阅读全文 »

在 iOS 开发中,weak property 或者是 __weak 修饰的变量,在对象释放后,变量会自动置为 nil。delegate 模式、block 中 weak strong dance 都会用到。

比如,声明一个 weak 的 delegate。

1
@property (nonatomic, weak) id delegate;

weak 用的好可以避免内存泄漏和野指针崩溃。weak 有这么大的作用,底层具体是咋实现的呢?本文探讨下 weak 的底层实现原理。

初探 weak 实现

首先通过汇编来看下,底层怎么实现的。先写几行代码。

1
2
3
4
5
{
id referent = [NSObject new];
__weak id weakObj = referent;
NSLog(@“%@“, weakObj);
}

在 Xcode 中勾选 Debug -> Debug Workflow -> Always Show Disassembly,然后在 NSLog 行加个断点,运行之后可以看到这样一段汇编代码:

disassembly code

阅读全文 »

在我们点击屏幕的时候,系统捕获到触摸事件,系统把包含这些触摸事件的信息包装成 UITouch 和 UIEvent 实例,然后找到当前运行的应用,逐级寻找能够响应这个事件的对象,直到没有响应者响应。这一系列响应者组成了响应链。

首先,系统捕获到点击行为后,将点击事件封装成 UIEvent 对象。接下来,就需要确定具体触摸到哪个 view,也即是找到手指触摸到的处于屏幕最前端的 view,这一步叫 hit-testing。

hit-testing

UIView 中有两个方法,hitTest 和 pointInside:

1
2
- (UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;

pointInside 方法是判断 point 是否在当前 view 内。
hitTest 方法是调用 pointInside 函数判断触点是否在当前 view 内,以及递归调用子 view 的 hitTest 方法,找到实际触摸的 view。

阅读全文 »

苹果在 iOS 9 开始提供 Universal Link 唤起 App 的能力。Universal Link 时机上是一个 https 链接,在用户点击网页中的 https 链接时,先尝试唤起 App,唤起失败则加载对应的网页。

相比于使用 scheme:// 这种 scheme 的方式唤起 App,Universal Link 唤起的优点在于苹果并未提供接口进行拦截,一般 App 不会进行拦截。并且在唤起失败时,能自动加载对应的页面。

因此,业内使用 Universal Link 唤起 App的逐渐增多。用户被恶意导流至其他 App 的问题日益严重。本文研究 iOS Universal Link 唤起 App 的逻辑,找到拦截 Universal Link 的方案。

阅读全文 »

这是设计模式系列第三篇博文,装饰者模式,也是《Head First 设计模式》的学习笔记。

定义

装饰者模式 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

问题

老套路,为了后面讨论的需要,先简单的复述下《Head First 设计模式》中的例子(简化版)。

  1. 星巴克中有一系列的咖啡(Coffee),其中一种为 DarkRoast。
  2. 咖啡可以配上不同的调料,假设有两种调料 Mocha 和 Whip。
  3. 顾客点咖啡之后,可以自主选择调料。

在这三个条件之下,要怎么来设计星巴克的点单系统,以计算出最后的价格?

可以把咖啡和调料都抽象成顾客需要消费的产品(Beverage),咖啡对象和调料对象都实现 Beverage 协议。

阅读全文 »

也不是经常使用 Python,每次用时都忘的差不多,又需要重新查资料。所以,开篇博客记录下这些琐碎的知识点。
都是入门基础级别的知识点,大神请出门左拐👋👋。

获取日期

Python lib doc: datetime

1
2
3
4
5
6
7
8
9
import datetime

# 按格式打印当前时间
print datetime.datetime.now().strftime('%m-%d')

# 按格式打印昨天时间
delta = datetime.timedelta(1)
yestoday = datetime.datetime.now() - delta
print yestoday.strftime('%m-%d')
阅读全文 »

这是设计模式系列第二篇博文,观察者模式,也是《Head First 设计模式》的学习笔记。

定义

主题(Subject):主题对象管理某些数据。
观察者(Observer):监听主题的数据变化,执行响应的操作。

观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

讨论

在日常开发中,经常用到观察者模式,相信大家都已经对观察者模式烂熟于心。但是是不是真的用的好呢?我提几个问题,看看大家是否有思考过,也欢迎留言讨论。

通知数据更新,是选择以推的方式来实现还是以拉的方式来实现?各有哪些优缺点?

我们先定义下推和拉。
推的方式:通知 Observer 数据有更新时,通知把更新后的数据通过参数一起传递过去;
拉的方式:数据更新时,只通知 Observer 数据有更新,具体需要哪些数据由 Observer 主动调 Subject 的接口查询。

拉的方式,Observer 并不知道具体更新的数据是哪些。收到数据更新时,只能一股脑的把自己需要的数据都重新查询一遍,有时候是很低效的。其次,有时候 Observer 需要知道更新前的数据以及更新后的数据,使用拉的方式也无法解决这种场景。

如果选择推的方式,以上两个缺点都可以解决。但是同时也带来了一个新的问题。在推的方式中,更新的数据是以参数来传递的。当需要新增参数时,就需要修改所有 Observer 的方法,略蛋疼。

但其实也不是没有解决方法,可以使用一个 model 来传递。当通知更新的参数大于三个时,建议以传递 model 的方式来通知更新。

阅读全文 »

最近 Vultr 的 VPS 越来越不给力,挂在 Vultr VPS 上的博客加载速度越来越慢。遂决定将博客迁回 github pages。

其实很简单,两步搞定:

  • 首先,在 github 博客仓库根目录下新建一个文件,文件名CNAME,文件内容为自定义的域名,例如:blog.duxevr.com
  • 在 DNS 服务商那增加一个CNAME记录,指向 github 博客的地址。
    github 博客的 url 为 YOUR-GITHUB-NAME.github.io,例如我的 github 博客url为:jefferyfan.github.io。

Hexo 配置相关文章:

在大学的时候,看过一本设计模式的书。那时是上网找的pdf,书名不记得了。但是印象最深刻的是,那本书特别晦涩难懂,翻译的特别拗口。虽然硬着头皮看完了,但是记住的没有多少。

如今,买了一本《Head First 设计模式》,开始重新学习设计模式。边学边记录,算是学习笔记吧。这篇是《Head First 设计模式》学习笔记第一篇:策略模式。
(好吧,我又开了一个坑,能不能坚持写完还另说。)

从问题入手

《Head First 设计模式》书中提到的Duck的例子非常的形象,我用自己的画重新表述一遍,跟书中略有区别。

  1. 需要实现一个鸭子Duck类,鸭子有一种行为:飞行
  2. 正常的鸭子会飞,但是玩具鸭不会飞。

我们很自然的想到,实现基类Duck类,提供一个默认的fly方法,玩具鸭重写fly方法,提供一个空实现。

或者是基类不提供fly方法实现,实现两个子类FlyableDuckToyDuck,其中FlyableDuck表示会飞的鸭子,实现fly方法;ToyDuck表示玩具鸭,提供空的fly方法。

就我列出的两点的要求来说,两个方法都可以实现。但有以下缺点:

  • 方法一:如果后续新增加一种不会飞的鸭子时,需要关心父类已经实现了对应的fly方法,需要主动覆盖fly方法
  • 方法二:如果后续新增一种叫的行为,分为吱吱叫、呱呱叫,飞行行为也分为蜻蜓点水飞行、低空飞行两种,那是不是就得实现四个类表示行为两两组合的鸭子呢?
阅读全文 »