热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Swift互用性:与Cocoa数据类型共舞(Swift2.0版)b

本节内容包括:字符串(Strings)数值(Numbers)集合类(CollectionClasses)错误(Errors)Foundation数据类型(FoundationDat

本节内容包括:

  • 字符串(Strings)

  • 数值(Numbers)

  • 集合类(Collection Classes)

  • 错误(Errors)

  • Foundation数据类型(Foundation Data Types)

  • Foundation函数(Foundation Functions)

  • Core Foundation

作为对 Objective-C 互用性(互操作性)的一部分,Swift提供快捷高效的方式来处理 Cocoa 数据类型。

Swift 会自动将一些 Objective-C 类型转换为 Swift 类型,以及将 Swift 类型转换为 Objective-C 类型。在 Objective-C 和 Swift 中也有一些具有互用性的数据类型。那些可转换的数据类型或者具有互用性的数据类型被称为bridged数据类型。举个例子,在 Swift 中,我们可以将一个Array值传递给一个要求为NSArray对象的方法。我们也可以转换一个 bridged 类型和它的副本。当我们使用as转换 bridged 类型或者那些由常量和变量所提供的类型时,Swift 会桥接它们的数据类型。

Swift 也提供一种简单便捷的覆盖方法来连接 Foundation 的数据类型,在后面的 Swift 语言中,我们能在它的句法中感受到自然和统一。

字符串

Swift会在String类型和NSString类型中自动转换。这意味着在可以使用NSString对象的地方,我们可以使用一个属于 Swift 的String类型代替它,这样做会同时拥有它们数据类型的特点,比如说String类型的字符串插值,基于Swift设计的API,以及NSString类的多种功能。因此,我们几乎不必再在我们的代码中使用NSString类。事实上,当 Swift 接入 Objective-C API 时,它将把所有NSString类型替换为String类型。当我们在我们的Objective-C代码中使用 Swift 类时,接入的API会将所有String类型替换成NSString类型。

为了允许字符串转换,只需导入Foundation框架。举个例子,我们在 Swift的一个字符串中可以访问capitalizedString,这是NSString类的一个属性,然后 Swift 会自动将String转换为一个NSString对象来访问这个属性。这个属性甚至会返回一个 Swift的String类型,因为它在导入的时候被替换了。

1
2
3
4
import Foundation
let greeting = "hello, world!"
let capitalizedGreeting = greeting.capitalizedString
// capitalizedGreeting: String = Hello, World!

如果我们确实需要用到一个NSString对象,我们可以用一个 Swift 的String值并转换它。String类型总是可以从一个NSString对象转换为一个Swift的String的值,因此,再没有必要去使用一个可选的类型转换器(as?)。我们也可以在一个字符串中通过定义常量和变量来创建一个NSString对象。

1
2
3
4
5
6
import Foundation
let myString: NSString = "123"
if let integerValue = Int(myString as String) {
    print("\(myString) is the integer \(integerValue)")
}
// prints "123 is the integer 123"

本地化

在Objective-C中,常用NSLocalizedString类的宏来定位一个字符串。这集合的宏包括NSLocalizedString,NSLocalizedStringFromTable,NSLocalizedStringFromTableInBundle,和NSLocalizedStringWithDefaultValue。而在Swift中,只用一个函数就可以实现跟整个NSLocalizedString集一样的功能,即NSLocalizedString(key:tableName:bundle:value:comment:)。这个NSLocalizedString函数分别为tableName,bundle和value参数提供了一个默认值。我们可以用它来替换宏。

数值

Swift会自动将已确定的数值类型Int和Float转换为NSNumber。这样的转换允许我们基于其中一种类型创建一个NSNumber:

1
2
let n = 42
let m: NSNumber = n

我们也能传递一个Int类型的值,比如传递给一个要求为NSNumber类型的参数。同时需要注意的是,NSNumber可以包含多种不同的类型,因此我们不能把它传递给单一的一个Int值。

下面所列出的类型都会自动转换为NSNumber:

  • Int

  • UInt

  • Float

  • Double

  • Bool

集合类

Swift 会自动将NSArray、NSSet和NSDictionary类转换为Swift里等价的类:Array、Set和Dictionary。这意味着我们将受益于Swift强大的算法和得天独厚的语法来处理集合--可互相转换的 Foundation 和 Swift 集合类型。

数组(Arrays)

Swift 会在Array类型和NSArray类型中自动转换。当我们从一个 Swift 数组转换成一个NSArray对象后,转换后的结果则是一个[ObjectType]类型的数组。如果NSArray对象没有指明一个确切的参数类型,那么它将会转换成[AnyObject]类型的Swift数组。

比如说,我们看到以下Objective-C声明:

1
2
3
@property NSArray* dates;
- (NSArray *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray *)timestamps;

那么,转换为Swift,则是这样子的:

1
2
3
var dates: [NSDate]
func datesBeforeDate(date: NSDate) -> [NSDate]
func addDatesParsedFromTimestamps(timestamps: [String])

如果某个对象是 Objective-C 或者 Swift 类的实例,或者这个对象可以转换成另一种类型,那么这个对象则属于AnyObject类型的对象。我们可以将任一NSArray对象转换成一个 Swift 数组,因为所有 Objective-C 的对象都是AnyObject类型的。正因如此,Swift 的编译器会在接入 Objective-C APIs 的时候将NSArray类替换成AnyObject[]。

当我们将一个NSArray对象转换成一个 Swift 数组后,我们也可以将数组强制类型转换成一个特定的类型。与从NSArray类转换到AnyObject[]不同的是,从AnyObject类型的对象转换成明确的类型并不会保证成功。由于直到运行时编译器才知道AnyObject的对象能否被强制转换为特定的类型,因此,从AnyObject[]转换为SomeType[]会返回一个 optional 的值。举个例子,如果我们知道一个Swift数组只包含UIView类的实例(或者一个UIView类的子类),我们可以将AnyObject类型的数组元素强制转换为UIView对象。如果Swift数组中的元素在运行时不是UIView类型的对象,那么转换则会返回nil。

1
2
3
4
let swiftyArray = foundationArray as AnyObject[]
if let downcastedSwiftArray = swiftArray as? UIView[] {
    // downcastedSwiftArray contains only UIView objects
}

我们也可以在for循环中将NSArray对象定向地强制转换为特定类型的Swift数组:

1
2
3
for aView: UIView! in foundationArray {
     // aView is of type UIView
}

注意:这种转换是强制转换,如果转换不成功则会在运行时产生错误信息。
当我们从 Swift 数组转换为NSArray对象时,Swift 数组里的元素必须是属于AnyObject的。例如,一个Int[]类型的 Swift 数组包含Int结构的元素。Int类型并不是一个类的实例,但由于Int类型转换成了NSNumber类,Int类型属于AnyObject类型的。因此,我们可以将一个Int[]类型的Swift数组转换为NSArray对象。如果 Swift 数组里的一个元素不属于AnyObject类型,那么在运行时就会产生错误。

我们也可以从 Swift 数组中创建一个NSArray对象。当我们将一个常量或变量定义为一个NSArray对象并分配一个数组给它作为实例变量时,Swift 将会创建 NSArray对象,而不是 Swift 数组。

1
2
let schoolSupplies: NSArray = ["Pencil""Eraser""Notebkko"]
// schoolSupplies is an NSArray object containing NSString objects

上面的例子中,Swift 数组包含包含三个String字符串。由于从String类型转换为NSString类,数组字面量被转换成一个NSArray对象,并成功分配给schoolSupplies变量。

当我们在 Objective-C 代码中使用 Swift 类或者协议时,接入的API会将全部所有类型的Swift数组代替为NSArray。若我们将一个NSArray对象传递给Swift的API并要求数组元素为一个新的类型,运行时就会产生错误。如果 Swift API 返回一个不能被转换为NSArray类型的 Swift 数组,错误也会随之产生。

集合(Sets)

除了数组以外,Swift还会自动在Set类型和NSSet类之间进行转换。当我们将一个带有参数类型的NSSet对象转换为Swift集合之后,得到的结果是Set类型的集合。而如果NSSet对象没有指明其参数类型,那么它将会转换为Set类型的Swift集合。

比如说,我们看到以下Objective-C声明:

1
2
3
@property NSSet* words;
- (NSSet *)wordsMatchingPredicate:(NSPredicate *)predicate;
- (void)removeWords:(NSSet *)words;

那么,转换为Swift,则是这个样子的:

1
var words: Setfunc wordsMatchingPredicate(predicate: NSPredicate) -> Setfunc removeWords(words: Set)

我们能够将所有NSSet对象转换为Swift集合,因为所有的Objective-C对象都可以被转换为AnyObject。所有的NSSet对象都能够转换为Swift对象,因此Swift编译器将会在导入Objective-C API的时候,将所有的NSSet类转换为Set。同理,当我们在Objective-C中使用Swift类或者协议的时候,导入器将会将Swift集合重新映射为Objective-C兼容的类型:NSSet对象。

当我们将NSSet对象转换为Swift集合后,还可以将集合下转为其他指定类型。就如同Swift数组的下转一样,Swift集合的下转不确保一定成功。对Set下转为指定类型的结果需要使用as?操作符,以确保其是可选值。

我们仍然可以直接从Swift数组字面量中直接创建一个NSSet对象,它同样遵循上面提到的转换规则。当我们明确地将某个常量或者变量定义为NSSet对象,并且使用一个数组字面量来赋值的时候,Swift将会创建一个NSSet对象,而不是Swift集合。

字典(Dictionaries)

Swift同样可以在Dictionary和NSDictionary类当中自动转换。当我们将带有参数类型的NSDictionary对象转换为Swift字典之后,得到的结果是一个[ObjectType]类型的字典。如果NSDictionary对象没有指明参数类型,那么它将会被转换为[NSObject:AnyObject]类型的Swift字典。

比如说,我们看到以下Objective-C声明:

1
2
3
@property NSDictionary* cachedData;
- (NSDictionary *)fileSizesForURLsWithSuffix:(NSString *)suffix;
- (void)setCacheExpirations:(NSDictionary *)expirations;

那么,转换为Swift,则是这个样子的:

1
2
3
var cachedData: [NSURL: NSData]
func fileSizesForURLsWithSuffix(suffix: String) -> [NSURL: NSNumber]
func setCacheExpirations(expirations: [NSURL: NSDate])

我们能够将所有的NSDictionary对象转换为Swift字典,因为所有的Objective-C对象都兼容AnyObject。重申一下,某个对象能够“兼容”AnyObject,指的是其是Objective-C的一个实例,或者是Swift的类,亦或者是能够转换为这两者之一的东西。所有的NSDictionary对象都能够转换为Swift字典,因此Swift编译器会在导入Objective-C API的时候,将所有的NSDictionary类替换成[NSObject: AnyObject]。同理,当我们在Objective-C中使用Swift类或者协议的时候,导入器将会将Swift字典重新映射为Objective-C兼容的类型:NSDictionary对象。

当我们将NSDictionary对象转换为Swift字典后,还可以将字典下转为其他指定类型。就如同Swift数组的下转一样,Swift字典的下转不确保一定成功。对[NSObject: AnyObject]下转为指定类型的结果需要使用as?操作符,以确保其是可选值。

当我们进行反向转换,也就是将Swift字典转换为NSDictionary对象的过程中,其键值都必须是某个类的实例,或者能够被转换为某个类的实例。

我们仍然可以直接从Swift数组字面量中直接创建一个NSDictionary对象,它同样遵循上面提到的转换规则。当我们明确地将某个常量或者变量定义为NSDictionary对象,并且使用一个数组字面量来赋值的时候,Swift将会创建一个NSDictionary对象,而不是Swift字典。

错误

Swift能够自动在ErrorType类型和NSError类之间转换,会发生错误的Objective-C方法等价于Swift中的throw方法,而会发生错误的Swift方法通过Objecitive-C错误约定,也等价于产生错误的Objective-C方法。

实现ErrorType协议,并且使用@objc特性声明的Swift枚举类型,会产生一个NS_ENUM声明和一个NSString常量,以能够在产生的头文件中设定对应的错误范围。比如说,有以下Swift枚举声明代码:

1
2
3
@objc public enum CustomError: Int, ErrorType {
    case A, B, C
}

那么,在相应的生成头文件中的Objectivive-C声明就是:

1
2
3
4
5
6
7
// Project-Swift.h
typedef SWIFT_ENUM(NSInteger, CustomError) {
  CustomErrorA = 0,
  CustomErrorB = 1,
  CustomErrorC = 2,
};
static NSString * const CustomErrorDomain = @"Project.CustomError";

关于更多Swift和Objective-C API中错误处理的相关信息,请参阅Error Handling。

Foundation数据类型

Swift 也提供一种简单便捷的覆盖方法来连接定义在 Foundation 框架中的数据类型。在CGSize和CGPoint中使用覆盖方法,我们就能在它的句法中感受到Swift语言的自然和统一。比如,我们可以使用如下语法创建一个CGSize类型的结构:

1
let size = CGSize(width: 20, height: 40)

覆盖方法也允许我们以一种自然的方式调用 Foundation 的结构函数。

1
2
3
let rect = CGRect(x: 50, y: 50, width: 100, height: 100)
let width = rect.width    // equivalent of CGRectGetWidth(rect)
let maxX = rect.maxY      // equivalent of CGRectGetMaxY(rect)

Swift可以将NSUInteger和NSInteger转换为Int类型。这些类型都会在 Foundation APIs 中变为Int类型。在 Swift 中Int常被尽可能地用以连贯性,同时当我们要求一个无符号整数类型时,UInt类型总是可使用的。

Foundation函数

在 Swift 中,NSLog可在系统控制台输出信息。我们可以像在 Objective-C 中使用过的语法格式那样使用此函数。

1
2
NSLog("%.7f", pi)
// Logs "3.1415927" to the console

同时,Swift 也提供像print(_:)这样的输出函数。多亏于 Swift 的字符插值机制才让这些函数简单,粗暴,高效。这些函数不会在系统控制台输出信息,但在需要调用的时候却是可用的。

Swift 中不再存在NSAssert函数,取而代之的是assert函数。

Core Foundation

Swift中的 Core Foundation 类型是一个成熟的类。当出现内存管理注释时,Swift 会自动地管理 Core Foundation 对象的内存,这其中包括我们实例化了的 Core Foundation 对象。在 Swift 中,我们可以自由变换 Fundation 和 Core Foundation 类型。如果我们想先转换为桥接 Foundation 类型时,那么也可以桥接一些 toll-free bridged Core Foundation 类型到 Swift 标准库类型。

重映射类型(Remapped Types)

当 Swift 导入 Core Foundation 类型时,编译器会重映射导入的类型名字。编译器会从每个类型名字的末端移除 Ref,这是因为所有的 Swift 类都属于引用类型,因此后缀是多余的。

Core Foundation 中的CFTypeRef类型会对Anyobject类型重映射。所以我们以前使用的CFTypeRef,现在该换成AnyObject了。

内存管理对象(Memory Managed Objects)

在 Swift 中,从 annotated APIs 返回的 Core Foundation 对象能够自动进行内存管理--我们不再需要调用自身的CFRetain,CFRelease,或者CFAutorelease函数。

如果我们从自身的C函数和 Objective-C 方法中返回一个 Core Foundation 对象,我们需要用CF_RETURNS_RETAINED或者CF_RETURNS_NOT_RETAINED注释这个对象。我们同样可以使用CF_IMPLICIT_BRIDGING_ENABLED和CF_IMPLICIT_BRIDGING_DISABLED宏命令来封装C函数声明,这些函数遵循Core Foundation的管理规定、命名规定,以便能够根据命名退导出内存管理机制能。

如果我们只调用那些不会间接返回 Core Foundation 对象的 annotated APIs,那么现在我们可以跳过本节的剩余部分了。否则,下面我们继续学习非托管的 Core Foundation 对象。

非托管对象(Unmanaged Objects)

当 Swift 导入 unannotated 的APIs时,编译器将不会自动地对返回的 Core Foundation 对象进行内存管理托管。Swift 将这些返回的 Core Foundation 对象封闭在一个Unmanaged结构中。那些间接返回 Core Foundation 的对象也是非托管的。举个例子,这里有一个 unannotated 的 C 函数:

1
CFStringRef StringByAddingTwoStrings(CFStringRef string1, CFStringRef string2)

这里说明了Swift是怎么导入的:

1
func StringByAddingTwoStrings(CFString!, CFString!) -> Unmanaged!

假设我们从 unannotated APIs 接收了非托管的对象,在使用它之前,我们必须将它转换为能够内存管理的对象。在这方面,Swift 可以帮我们进行内存管理而不用自己动手。同时,Unmanaged结构也提供了两个方法来把一个非托管对象转换为一个可内存管理的对象--takeUnretainedValue()方法和takeRetainedValue()方法。这两个方法会返回原始的,非封闭的对象类型。我们可以根据我们实际调用的APIs返回的unretained或retained的对象,来选择哪一方法更合适。

比如,假设这里有一个 C 函数,这个函数在返回值前不会释放CFString对象。在使用这个对象前,我们使用takeUnretainedValue()函数,以将它转换为一个能够内存管理托管的对象。

1
2
let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()
// memoryManagedResult is a memory managed CFString

我们也可以在一个非托管的对象中使用retain(),release()和autorelease()方法,但是这种做法并不值得推荐。

要了解更多信息,请参考Memory Management Programming Guide for Core Foundation中的Memory Management Programming Guide for Core Foundation一节。

Swift互用性:与 Cocoa 数据类型共舞(Swift 2.0版)-b


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
author-avatar
飛373227470
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有