iOS 从零开始使用 Swift:探索 Foundation 框架

iOS 从零开始使用 Swift系列:
探索 iOS SDK
探索 Foundation 框架
使用 UIKit 的第一步
自动布局基础
表格视图基础
导航控制器和视图控制器层次结构
iOS 上的数据持久性和沙盒
构建购物清单应用程序 1
构建购物清单应用程序 2
下一步该去哪里

Foundation 框架是 iOS 开发人员工具箱中的面包和黄油。它NSObject为 iOS 开发提供了根类和大量基本构建块,从数字和字符串类到数组和字典。Foundation 框架一开始可能看起来有点乏味,但它利用了很多功能,对于开发 iOS 应用程序是必不可少的。

关于核心基础的一句话

上一篇文章中,我简单地提到了 Core Foundation 以及它与 Foundation 的关系。尽管我们不会在本系列的其余部分中明确使用 Core Foundation 框架,但熟悉该框架并了解它与您将广泛使用的面向对象的兄弟 Foundation 有何不同是很有用的。

虽然 Foundation 框架是在 Objective-C 中实现的,但 Core Foundation 框架是基于 C 的。尽管存在这种差异,Core Foundation 框架确实实现了有限的对象模型。这个对象模型允许定义一组通常被称为对象的不透明类型——尽管严格来说它们不是对象。

两个框架的主要目标是相似的,支持在各种库和框架之间共享数据和代码。Core Foundation 还包括对国际化的支持。这种支持的一个关键组件是通过 opaque 类型提供的,它有效地管理Unicode字符CFString 数组。

正如我之前提到的,免费桥接通过在函数参数中启用 Cocoa 对象替换 Core Foundation 对象,从字面上弥合了两个框架之间的差距,反之亦然。

重要的是要注意自动引用计数(ARC)不管理核心基础“对象”。这意味着您在使用 Core Foundation 时负责管理内存。 Mike Ash 写了 一篇 关于自动引用计数以及如何将 ARC 与 Core Foundation 和免费桥接一起使用的精彩文章。

 如果您想了解有关该框架的更多信息以及该框架的不透明类型的完整列表,请访问 Apple 的 Core Foundation 框架参考。

实践,实践,实践

学习新技能最好通过练习来完成。让我们从在 Xcode 中创建一个新的 Playground 开始吧。将操场命名为Foundation并将 Platform设置为 iOS。单击 下一步 继续。告诉 Xcode 您要将 Playground 保存在哪里,然后单击 Create

这就是操场的内容应该是什么样子。

//: Playground - noun: a place where people can play<font></font>
<font></font>
import UIKit<font></font>
<font></font>
var str = "Hello, playground"

Playground 包含顶部的注释、  UIKit 框架的导入语句和变量声明。在右侧,您可以看到操场的输出。

由于我们将使用 Foundation 框架,因此我们需要将 UIKit 的 import 语句替换为 Foundation 的 import 语句。删除顶部的注释,更新导入语句,并删除变量声明。

import Foundation

基础

Foundation 框架不仅仅是用于处理数字、字符串和集合(数组、字典和集合)的类的集合。它还定义了数十种协议、函数、数据类型和常量。

在本文的其余部分,我将主要关注您在开发 iOS 应用程序时最常使用的类。但是,我还将简要介绍 Foundation 框架定义的三个关键协议 NSObject,  NSCoding、 和 NSCopying.

协议

多种语言(例如 Perl、Python 和 C++)提供对 多重继承的支持。这意味着一个类可以从多个类继承——成为多个类的子类。

尽管 Swift 和 Objective-C 不提供对多重继承的支持,但它们确实通过协议形式的规范支持多重继承。我们已经在本系列的前面介绍了协议,但是还有一个重要的细节我们还没有讨论。

可选和必需的方法

请记住,Swift 协议的每个方法都是必需的。如果符合 Swift 协议的类型没有实现协议定义的每个属性和方法,编译器将抛出错误。这不适用于 Objective-C 协议。在 Objective-C 中,协议的方法可以标记为可选。如果协议方法是可选的,则不需要符合协议的类型来实现该方法。

因为 iOS SDK 的绝大多数框架都是用 Objective-C 编写的,所以我们会遇到一些定义可选方法的 Objective-C 协议。重要的是要记住 Swift 协议的每个属性和方法都是必需的。

好处

协议的好处是多方面的。当一个类 采用 或 符合 协议时,该类应该实现协议声明的方法。对于你们中的一些人来说,协议可能会让人想起 Java 中的接口。这意味着可以使用协议来声明对象的接口,而无需透露对象的类型。

多重继承有它的好处,但它肯定也有 它的缺点。协议的优点是不相关的类型、枚举、类和结构,仍然可以通过使用协议共享相似的行为。

NSObject

除了 NSObject 根类之外,Foundation 框架还定义了 NSObject 协议。然而,在 Swift 中,类和协议不能具有相同的名称。因此,该协议 在 SwiftNSObject中被称为。NSObjectProtocol

符合 NSObjectProtocol 协议的对象可以被询问其类和超类,可以与其他对象进行比较,并对 self. 这只是添加到符合 NSObjectProtocol 协议的对象的行为的一小部分。

NSCoding

符合 NSCoding 协议的对象可以进行编码和解码。这对于需要归档或分发的对象是必要的。例如,对象归档发生在对象或对象图存储到磁盘时。

NSCopying

该 NSCopying 协议仅声明了一种方法,  copyWithZone(_:). 如果一个类要支持复制对象,它需要符合 NSCopying 协议。复制对象是通过向其发送 copy() or 的消息来完成的copyWithZone(_:)

课程

NSObject

该类 NSObject 是绝大多数 Objective-C 类层次结构的根类。如果我们对 Swift 感兴趣,为什么这很重要?请记住,iOS SDK 的大多数框架都是由 Objective-C 驱动的。即使您决定使用 Swift 开发应用程序,您仍然会在后台使用 Objective-C。

通过从 NSObject 根类继承,对象知道如何表现得像 Objective-C 对象以及如何与 Objective-C 运行时交互。NSObject 符合 NSObject/NSObjectProtocol 我们刚才讨论过的协议应该不足为奇 。

让我们看看如果一个类继承自 NSObject该类,我们会免费获得什么。将以下代码片段添加到您的 Playground。如果您已阅读本系列的前几篇文章,这应该看起来很熟悉。我们声明一个Book继承自的类NSObject。该类Book实现了两个实例方法,chapters()并且pages().

class Book: NSObject {<font></font>
    func chapters() {<font></font>
        <font></font>
    }<font></font>
    <font></font>
    func pages() {<font></font>
        <font></font>
    }<font></font>
}

因为Book继承自NSObjectNSObject符合NSObjectProtocol 协议,所以我们可以向Book该类询问其行为和继承树。看看下面的例子。

我们首先通过调用类来询问类Book本身。该方法是一个类方法,这意味着我们可以在类本身 上调用该方法。接下来,我们询问该类的超类或父类,即它直接继承的类。这将返回一个可选的,因为并非每个类都有一个父类。不出所料, is 的超类。classForCoder()BookclassForCoder()BookBookBookNSObject

下一行告诉我们Book 符合NSObjectProtocol协议,这并不意外,因为它从NSObject类继承了这个特征。我们还了解到Book不符合NSCoding协议。

该类似乎Book没有响应该chapters()方法。这并不奇怪,因为chapters()它是实例方法,而不是类方法。我们的示例的最后两行证明了这一点,其中我们实例化了一个Book实例并向该实例询问相同的问题。

在上面的例子中我们使用了respondsToSelector(_:)方法,但是你可能想知道选择器是什么?“响应选择器”是什么意思?选择器是函数或方法名称的一个花哨的词。Objective-C 运行时使用选择器向对象发送消息。长话短说,如果您阅读单词选择器,请考虑功能或方法。更精细的细节超出了本教程的范围。

NSNumber

该类 NSNumber 是管理任何基本数值数据类型的实用程序类。它是该类的子 NSValue 类,它为标量类型、指针、结构和对象 ID 提供面向对象的包装器。该类 NSNumber 定义了用于检索其存储的值、比较值以及返回存储值的字符串表示形式的方法。

请记住,从 NSNumber 实例中检索的值需要与存储在其中的值一致。该类 NSNumber 将尝试将存储的值动态转换为请求的类型,但不言而喻, NSNumber 可以管理的数据类型存在固有的限制。

让我用一个例子来说明这一点。将以下代码片段添加到您的 Playground。

NSNumber 我们通过将 Double 值 传递给 来创建一个新 实例init(double:)。接下来,我们使用三种不同的 NSNumber 方法请求存储的值。如您所见,结果并不总是反映存储在 myNumber.

课程很简单, NSNumber 通过跟踪存储在 NSNumber 实例中的类型在使用时保持一致。

NSString

类的实例 管理 组成文本字符串的字符NSString 数组 。 与管理字符 unichar的常规 C 字符串的细微但重要的区别 在于, 字符是多字节字符。这类似于 Swift 的类型。charunicharString

顾名思义, unichar 字符非常适合处理 Unicode 字符。由于这种实现, NSString 该类为国际化提供了开箱即用的支持。

我想强调的是,由 NSString 实例管理的字符串是不可变的。这意味着,一旦创建了字符串,就无法修改它。来自其他语言(例如 PHP、Ruby 或 JavaScript)的开发人员可能会对这种行为感到困惑。

Foundation 框架还定义了 , 的可变子类 NSString, NSMutableString可以在初始化后进行修改。

有多种方法可以创建字符串对象。创建字符串对象的最简单方法是调用 类string() 上的方法 NSString 。这将返回一个空字符串对象。查看 类参考 以 NSString 获取完整的初始化程序列表。

创建字符串对象的另一个常见途径是通过字符串文字。在下一个示例中,将字符串文字分配给stringA。在编译时,编译器会将字符串文字替换为 NSString.

请注意,我们不依赖 Swift 的类型推断来确定stringA. 第二个例子,stringB说明了为什么会这样。如果我们没有明确指定stringAas的类型NSString,Swift 认为我们想要创建一个String实例。该示例还说明了从while不NSString继承。请记住,Swift 中的类型是结构体,而结构体不支持继承。NSObjectStringString

该类NSString具有广泛的用于创建和操作字符串的实例和类方法,如果有的话,您很少会觉得需要子类化NSString

让我们通过将以下代码段添加到您的 Playground 来探索 NSString 它的可变子类 。NSMutableString

let stringA: NSString = "This is "<font></font>
let stringB: NSString = NSString(string: "a mutable string.")<font></font>
<font></font>
var mutableString = NSMutableString(string: stringA)<font></font>
mutableString.appendString(stringB as String)

我们首先创建两个 NSString实例,一个使用文字语法,一个使用init(string:)初始化程序。然后通过将第一个字符串作为参数传递来创建可变字符串。为了说明可变字符串可以在创建后修改, stringB 附加到可变字符串并打印其值。

NSArray 和 NSSet

该类 NSArray 管理一个不可变的、有序的对象列表。Foundation 框架还定义了 , 的可变子 NSArray类 NSMutableArray。该类的 NSArray 行为非常类似于 C 或 Swift 数组,不同之处在于 NSArray 管理对象的实例。该类 NSArray 声明了多种有助于处理数组的方法,例如在数组中查找和排序对象的方法。

重要的是要了解 、 和 的实例 NSArray只能 NSSet存储 NSDictionary 对象。这意味着不可能在这些集合类或它们的子类中存储标量类型、指针或结构,如果这样做,编译器会抛出错误。解决方案是将标量类型、指针和结构包装在  我们在本文前面看到的NSValue 或 类似的实例中。NSNumber

将以下代码片段添加到您的 Playground 以进行探索 NSArray 及其可变对应物 NSMutableArray.

let myArray = NSArray(objects: "Bread", "Butter", "Milk", "Eggs")<font></font>
print(myArray.count)<font></font>
print(myArray.objectAtIndex(2))<font></font>
<font></font>
var myMutableArray = NSMutableArray(object: NSNumber(int: 265))<font></font>
myMutableArray.addObject(NSNumber(int: 45))

我们使用init(objects:) 初始化器创建一个数组。此方法接受可变数量的参数。接下来,我们打印数组中对象的数量,并在数组中询问 index 处的对象2

因为 NSMutableArray 继承自 NSArray,所以它的行为方式与 NSArray. 主要区别在于初始化后可以从数组中添加和删除对象。

在继续之前,我想说几句话 NSSet。此类与 类似 NSArray,但主要区别在于集合管理的对象集合是无序的,并且集合不能包含重复项。

的优点 NSSet 是如果你只需要知道一个对象是否包含在集合中,查询它的对象会更快。Foundation 框架还定义了 NSOrderedSet. 此类的实例具有 的优点 NSSet,而且还可以跟踪每个对象的位置。

NSDictionary

与数组一样,字典是大多数编程语言中的常见概念。例如,在 Ruby 中,它们被称为哈希。基本概念很简单,字典管理键值对或条目的静态集合。

与 Ruby 哈希一样,条目的键本身不需要是字符串对象。它可以是符合 NSCopying 协议的任何类型的对象,只要键在字典中是唯一的即可。但是,在大多数情况下,建议使用字符串对象作为键。

像数组一样,字典不能存储空值。如果要表示空值,则可以使用 NSNull. 该类 NSNull 定义了一个单例对象,用于对数组、字典和集合中的空值进行符号化。

单例模式是许多编程语言中的重要模式。它将类的实例化限制为一个对象。在开发 iOS 应用程序时,您会经常处理单例对象。

就像 NSArray,Foundation 框架定义了一个可变的 , 子 NSDictionary类 NSMutableDictionary。实例化字典有多种方法。看看下面的代码片段。

let keyA: NSString = "myKey"<font></font>
let keyB: NSString = "myKey"<font></font>
<font></font>
let myDictionary = NSDictionary(object: "This is a string literal", forKey: keyA)<font></font>
<font></font>
print(myDictionary.objectForKey(keyB))

我们声明了两个NSString包含相同字符串的单独对象。然后我们通过调用来实例化一个字典init(object:forKey:)。接下来,我们向字典询问与内容相关联的对象 keyB 并打印其值。

注意细节很重要。即使我们用作 keyA 键值对的键并 keyB 用作获取键值对的值或对象的键,字典仍为我们提供了正确的对象(作为可选)。该类 NSDictionary 足够聪明,知道我们想要与 string 关联的对象 "myKey"。这是什么意思?尽管对象 keyA 和 keyB 是不同的对象,但它们包含的字符串是相同的,而这正是 NSDictionary 类用来引用对象的字符串。

在结束本教程之前,我想向您展示一个嵌套字典和数组的示例以及一个NSMutableDictionary. 下面的代码片段展示了一个字典可以包含另一个字典或数组,它还展示了如何使用可变字典。

let myMutableDictionary = NSMutableDictionary()<font></font>
myMutableDictionary.setObject(myDictionary, forKey: "myDictionary")

结论

尽管我们在本文中涵盖了很多内容,但我们几乎没有触及 Foundation 框架必须提供的表面。不过,无需了解 Foundation 框架中定义的每个类或函数的详细信息即可开始 iOS 开发。在探索 iOS SDK 时,您将了解有关 Foundation 框架的更多信息。

在下一篇文章中,我们将探索 UIKit 框架,我还将讨论 iOS 应用程序的来龙去脉。

ios-from-scratch-with-swift-exploring-the-foundation-framework–cms-25155