Core Services
0x00 前言
这篇文章是自己在学习 CFHipsterRef Chapter 7 Core Services 时做的笔记。
0x01 Core Services
让我们花一点时间来真正地思考一下一个文件是什么。
从根本上来说,文件是一个信息资源。它以这样一种方式持续存在,即它在最初创建它的程序的范围之外保持持久可用。文件本身通常被编码成一维字节数组(one-dimensional byte array),理想地,在存储介质连续的部分中。
文件本身是无意义的 0 和 1。只有通过程序读和写,它们才是有意义的。
交换格式(特别是图像编码,如 PNG,GIF 和 JPEG)的一种常见做法是在文件开头使用一个唯一值,也称为文件签名或者“魔术数字”。关于一个文件的信息,例如其名称,大小和其他属性由文件系统存储为元数据(metadata)。从文件读取的程序使用它来确定如何解析和解释文件的数据。
这种信息如何被结构化和编码是文件系统的主要不同因素之一。
0x02 Data & Resource Forks
旧式的 Apple Macintosh Filesystem(MFS)为每一个文件条目关联(associated)了一个 data fork,resource fork 和多个 named forks。对于应用程序,data fork 将保存二进制可执行文件,而 resource fork 将包含图标位图和本地化字符串等内容。不是所有的文件都有 resource forks,但是它们对于将内容从表示(presentation)分离很有用,就像在字处理文档中一样。
在 Mac OS 上,文件类型和创建者代码(creator codes)由一个 OSType
表示,一个四字节标识符,最常被编码为四个 ASCII / Macintosh 罗马字符。文件类型的常见示例如下所示:
OSType of Common File Types
CODE | Executable Binary |
---|---|
Text | Plain Text |
PICT | QuickDraw Image |
SND | Sound |
OSType
还用于表示粘贴板内容的类型,错误代码和 AppleEvents,System 7 中引入的进程间通信机制。
实际上,最后一点关于 OSType
被重用的任务(tasks),如处理应用程序之间复制-粘贴(copy-paste)引起的一个有趣的观点:文件只是数据传递的多种方式之一。
一个这样的例子是通过互联网下载的信息。
在如今的应用中完成的绝大多数网络都是通过 HTTP。虽然资源的类型有时候可能从 URI 的扩展中收集,但是规范标识符要在 Content-Type
HTTP 头部字段(HTTP header field)中找到。此标头(header)的值使用 MIME 类型。MIME 类型是由中央管理机构定义的,互联网号码分配机构(The Internet Assigned Numbers Authority, IANA)也管理根名称服务器(root name servers)和 IP 地址块(IP address blocks)。
因此,任何取代 OSType
的系统都必须能够容纳互联网媒体以及现有的文件类型。
改系统是通用类型标识符(Universal Type Identifiers, UTI),并且它已被 OS X 引入。
0x03 UTI
UTI 提供了一个可扩展的层次化分类系统,可以提供开发者在处理即使非常奇葩的文件类型时极大的灵活性。例如,一个 Ruby 源文件(.rb)被分类为 “Ruby Source Code > Source Code > Text > Content > Data”;一个 QuickTime 视频文件(.mov)被分类为 “Video > Movie > Audiovisual Content > Content > Data”。
不限于文件,UTI 可以用于识别一些不同的实体(entities):
- 文件(Files)
- 目录(Directories)
- 粘贴板数据(Pasteboard Data)
- 包(Bundles)
- 框架(Frameworks)
- 互联网媒体(Internet Media)
- 流数据(Streaming Data)
- 别名和符号链接(Aliases and Symbolic Links)
0x04 Type Identifiers
公共域(The public domain)保留对大多数应用程序通用的常用或标准类型,例如:
- public.text
- public.plain-text
- public.jpeg
- public.html
0x05 Dynamic Type Identifiers
有时一个数据类型没有为其声明 UTI。UTI 通过创建一个动态的标识符来透明地处理这种情况。动态标识符(Dynamic Identifiers)具有域 dyn
(domain dyn),字符串后面其余部分是不透明的。它们可以被认为是一个 UTI 兼容封装器(UTI-compatible wrapper),有一个未知的文件名扩展,MIME 类型,OSType 等等。
0x06 Custom Type Identifiers
当创建一个自定义类型标识符时,目的是使 UTI 符合物理(a physical)和功能层次结构(functional hierarchy):
- 一个物理层次项目(item)的性质,例如它是一个文件还是一个目录。这应该继承自
public.item
。 - 一个功能层次与如何使用项目(item)有关。这不应继承自
public.item
,但是可以是像public
这样的。content
或public.executable
。
0x07 Working with UTIs
OS X 上的 Core Services 框架和 iOS 上的 Mobile Core Services 框架提供了根据通用类型标识符通过文件扩展和 MIME 类型来识别和分类数据类型的功能。
0x0701 Comparing
这里有两个比较 UTI 的方法。UTTypeEqual
执行相等判断,这相当于不区分大小的字符串比较。UTTypeConforms
则更深入一点,因为它查找功能和物理层次来找到一个匹配。这跟 isMemberOfClass:
和 isKindOfClass:
之间的区别一样。
1 | UTTypeConformsTo(CFSTR("public.jpeg"), CFSTR("public.item")); // YES |
0x0702 Copying Declarations
除了其唯一标识符之外,每个 UTI 都注册了一个属性(attributes)的属性(property)列表。这些属性可以使用 UTTypeCopyDeclaration
来检索:
1 | UTTypeCopyDeclaration(CFSTR("public.png")); |
0x0703 Converting
当使用 UTI 时,一个常见的任务是获取它们等效的 MIME 类型或者文件名扩展。这可以使用函数 UTTypeCopyPreferredTagWithClass:
来完成:
1 | NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(CFSTR("public.text"), kUTTagClassMIMEType); |
相反,确定 UTI 的一个 MIME 类型,可以使用 UTTypeCreatePreferredIdentifierForTag:
来完成:
1 | NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, CFSTR("jpg"), NULL); |
UTTypeCopyPreferredTagWithClass
& UTTypeCreatePreferredIdentifierForTag
的 tag 类参数可以是以下任意字符串常量:
1 | const CFStringRef kUTTagClassFilenameExtension; |
作为调解数据和文件传输成长和发展格局的一种手段,UTI 做的非常好。从像 “魔法数字” 签名和 resource forks 的特定文件系统机制到提供应用程序极大的灵活性在如何处理数据形式化的类型层次结构的转变。
充分使用 UTI 将确保应用程序负责任地与文件交互,并与其他东西相处得好。