前言
OC缺乏一个重要特性,不支持泛型。Swift拥有了这一特性,是灵活性的语法,在函数、结构体、类、枚举中都可以应用,相当于暂位符的作用,当类型暂时不确定,只有等到调用函数时才能确定具体类型的时候可以引入泛型。
简单理解泛型就是先占位,具体占位做什么,用的时候再说。Swift的Array和Dictionary类型都是泛型集
应用场景
简单模仿一个栈操作,主要实现出栈、入栈两个功能
// MARK: - 模仿栈
class TestStack {
var valueArray: [Int] = []
/// 压栈
func push(value: Int) -> () {
valueArray.append(value)
}
/// 出栈
func pop() -> (Int?) {
if let lastValue = valueArray.last {
valueArray.removeLast()
return lastValue
}else {
return nil
}
}
}
这样实现的栈只能操作Int类型,如果需求变更,要处理String类型,怎么解决?替换所有Int为String可以解决,但这种代码明显上不了台面。此外另外我们还会想到Any类型,如下
// MARK: - 模仿栈
class TestStack {
var valueArray: [Any] = []
/// 压栈
func push(value: Any) -> () {
valueArray.append(value)
}
/// 出栈
func pop() -> (Any?) {
if let lastValue = valueArray.last {
valueArray.removeLast()
return lastValue
}else {
return nil
}
}
}
如此TestStack可操作类型就不局限于一种了,但是带来了两个问题:1、数据类型安全问题;2、每次对栈进行操作时,都需要进行一系列繁琐的类型转换(casting操作,使用as来进行类型转换)
这里简单介绍下Any和AnyObject的区别:
在 Swift 3 之前,我们可以写完一个项目都只用 AnyObject 来代表大多数实例,好像不用与 Any 类型打交道。但事实上,Any 和 AnyObject 是有明显区别的,因为 Any 可以代表 struct、class、func 等等几乎所有类型,而 AnyObject 只能代表 class 生成的实例。
那为什么之前我们在 Swift 2 里可以用 [AnyObject] 声明数组,并且在里面放 Int、String 等 struct 类型呢?这是因为 Swift 2 中,会针对这些 Int、String 等 struct 进行一个 Implicit Bridging Conversions,在 Array 里插入他们时,编译器会自动将其 bridge 到 Objective-C 的 NSNumber、NSString 等类型,这就是为什么我们声明的 [AnyObject] 里可以放 struct 的原因。
但在 Swift 3 当中,为了达成一个门真正的跨平台语言,相关提案将 Implicit Bridging Conversions 给去掉了。所以如果你要把 String 这个 struct 放进一个 [AnyObject] 里,一定要 as NSString,这些转换都需要显示的进行了——毕竟 Linux 平台默认没有 Objective-C runtime。这样各平台的表现更加一致。当然这是其中一个目标。
参照泛型的特性,定义一个泛型类型。使用泛型后的示例代码如下:
`
// MARK: - 模仿栈
class TestStack<T> {
var valueArray: [T] = []
/// 压栈
func push(value: T) -> () {
valueArray.append(value)
}
/// 出栈
func pop() -> (T?) {
if let lastValue = valueArray.last {
valueArray.removeLast()
return lastValue
}else {
return nil
}
}
}
`
使用泛型:在初始化时通过明确的类型(这里用Int示例)来定义参数,之后编译器会将所有的泛型(T)替换成Int类型(如此实现的栈,最大优势在于能够匹配任何类型)。
// 示例1:Int
let myStack1 = TestStack<Int>()
myStack1.push(value: 1)
myStack1.push(value: 2)
myStack1.push(value: 3)
print(myStack1.pop() ?? "0")
// 示例2:String
let myStack2 = TestStack<String>()
myStack2.push(value: "a")
myStack2.push(value: "b")
myStack2.push(value: "c")
print(myStack2.pop() ?? "0")
泛型约束
泛型约束大致分为以下几种:
协议约束,泛型类型必须遵循某些协议
继承约束,泛型类型必须是某个类的子类类型
条件约束,泛型类型必须满足某种条件
协议约束
还拿TestStack栈说事儿,这里添加一个函数isContainValue,要判断栈中是否包含传入的元素,需要用到等式符(==)和不等符(!=)对任何两个该类型进行比较。Swift标准库中定义了一个Equatable协议,只有支持了这个协议才可以使用等式符(==)和不等符(!=),否则报红。所有的Swift标准类型自动支持Equatable协议,但是泛型由于不确定类型,系统不知对象是否支持Equatable协议,所以直接使用==或!=报红
解决方案,让TestStack栈中,让泛型T遵循Equatable协议
继承约束
这里定义了三个类Person、Leader、Coder,其中Leader是继承Person,
// MARK: - 人
class Person: NSObject {
func watchTV() -> () {
print("Person看电视:人民的名义")
}
}
// MARK: - 领导
class Leader: Person {
override func watchTV() -> () {
print("Leader看电视:人民的名义")
}
}
// MARK: - 程序员
class Coder {
func watchTV() -> () {
print("coder看电视:人民的名义")
}
}
func testWatchTV<T: Person>(obj:T) -> () {
obj.watchTV()
}
testWatchTV函数,接受一个泛型参数,要求该泛型类型必须继承Person,否则爆红。
条件约束
在类型名后面使用where来指定对类型的特殊需求,比如限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类等
// 定义一个协议
@objc protocol TestProtocol {
@objc optional func testOne() -> ()
}
// MARK: - 人
class Person: NSObject, TestProtocol {
var name: String?
func watchTV() -> () {
print("Person看电视:人民的名义")
}
}
// MARK: - 领导
class Leader: NSObject {
var name: String?
func watchTV() -> () {
print("Leader看电视:人民的名义")
}
}
testCondition函数要求传入的参数都是P类型的继承自Person,L类型继承自Leader,同时还附加了条件(where)这两个类都遵守了TestProtocol协议,由于Leader类型没有遵守TestProtocol协议,条件不满足所以爆红。修改方法则是Leader类遵守TestProtocol。
参考链接:http://www.jianshu.com/p/a907f0c09a60
参考链接:http://swift.gg/2015/09/16/swift-generics/