Swift之泛型

写在前面:

泛型,类似于OC中的id类型,它可以指代任何一种类型,并不是在编译时确定其类型,而是在运行时确定其实际类型。使用泛型的函数,我们称之为泛型函数

intout关键字

现有两变量,要求其值互换

var someStr = "I am A"
var anotherStr = "I am B"

如果方法中参数得值在方法内部不会发生变化
即参数为常量,用let声明,let可省略

func swapTwoStrings1( a: String, b: String) {
    let tempA = b //对形参a的值不发生变化
    let tempB = a  //对形参b的值不发生变化
}
someStr  //"I am A"  对实参someStr的值不发生变化
anotherStr  //"I am B"  对实参anotherStr的值不发生变化

如果方法中的参数的值在方法内部会发生变化
参数需声明为变量,用var声明,var不可省略

func swapTwoStrings2(var a: String, var b: String) {
    let temporaryA = a
    a = b  //方法内a的值发生了改变
    b = temporaryA  //方法内b的值发生了改变
}
swapTwoStrings2(someStr, b: anotherStr)
someStr  //"I am A"   方法外someStr的值未改变
anotherStr   //"I am B"  方法外anotherStr的值未改变

如果想让传入的参数在进入方法后发生改变(参数在进入方法后值发生真正改变)
使用inout关键字(使形参和实参指向的内存一致)

func swapTwoStrings3(inout a: String, inout b: String) {
    let temporaryA = a
    a = b  //方法内a的值发生了改变
    b = temporaryA  //方法内b的值发生了改变
}

//传递参数时多出现了"&"符号,其实传递了字符串的地址
swapTwoStrings3(&someStr, b: &anotherStr)
someStr     //"I am B"   方法外someStr的值发生改变
anotherStr  //"I am A"  方法外anotherStr的值发生改变

注意:inout修饰的参数是不能有默认值的,有范围的参数集合也不能被修饰,另外,一个参数一旦被inout修饰,就不能再被var和let修饰了

泛型

上面的方法是为了把两个String变量的值相互交换
现要求把两个Int变量的值相互交换

func swapTwoInts(inout a: Int, inout b: Int){
    let temporaryA = a
    a = b
    b = temporaryA
}

从方法结构来看,swapTwoStrings3和swapTwoInts两个方法仅仅是参数类型不同,想办法合并他们

//T代表占位类型,可以是任何一种类型,
//a与b类型都是T,T只能是一种类型,所以a与b的类型一定要相同
func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
swapTwoValues(&someStr, b: &anotherStr) //传入两个String类型
var someInt = 4
var anotherInt = 8
swapTwoValues(&someInt, b: &anotherInt)//传入两个Int类型
//swapTwoValues(&someStr, b: &anotherInt)  //报错a和b类型不同

使用多个泛型类型参数(在"<>"内用","分隔)

func toBeDict<KeyType,ValueType>(inout a: KeyType, inout b: ValueType) {
    Dictionary(dictionaryLiteral: (String(a), b))
}

toBeDict(&someStr,b: &someInt) //输出["I am A": 8]

泛型枚举
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)

用泛型来建立一个栈

//栈和Array相似,一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同push一个新值进栈)。同样的一个栈也只能从末端移除项(如同pop一个值出栈)
struct Stack<T> {
    //创建一个名为items的属性,使用空的T类型值数组对其进行初始化
    var items = [T]()
    //指定一个包含一个参数名为item的push方法,该参数必须是T类型
    mutating func push(item: T) {
        items.append(item)
    }
    //指定一个pop方法的返回值,该返回值将是一个T类型值
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var stackOfStrings = Stack<String>() //一个String栈
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.pop()  //"dos"
stackOfStrings.pop()  //"uno"

泛型约束语法

第一种,泛型类型后面追加协议

//Equatable协议规定了T的类型一定能使用"=="和"!="
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
    
    for (index, value) in EnumerateSequence(array) {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

findIndex([0,2,3,1], valueToFind: 1)

第二种,使用where约束

在实际使用where前,谈一下typealias(关联类型)

/*
typealias是一种常用于协议的特殊泛型,它可以使协议适用于更多情况
常规的写法应该是typealias MyInt = Int。表示给Int取一个别名为MyInt。
单独写typealias ItemType  代表给任意类型取别名为ItemType
协议中的关联类型的实际类型是不需要指定的,直到该协议接受。

注:这里的typealias不是用于约束泛型,而是为了下面的where约束泛型而服务的
*/
protocol Container {
    typealias ItemType
    //对象必须可以通过append方法添加一个新item到容器里;
    mutating func append(item: ItemType)
    //对象必须可以使用count属性获取容器里items的数量,并返回一个Int值
    var count: Int { get }  //get代表只读属性
    //对象必须可以通过容器的Int索引值下标可以检索到每一个item
    subscript(i: Int) -> ItemType { get }
}

//注意,这里满足Container约束的对象是StackPro结构体,而不是T
struct StackPro<T>: Container {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
    mutating func append(item: T) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> T {
        return items[i]
    }
}

var stackProOfStrings = StackPro<String>() //一个String栈
stackProOfStrings.push("uno")
stackProOfStrings.push("dos")
stackProOfStrings.count

扩展一个存在的类型为一指定关联类型(为了下面Array有ItemType这个属性)

//Array已经满足Container协议中的三个方法,其中的ItemType属性又是在运行时自动确定的类型,所以我们写一个空扩展就行了
extension Array: Container {}

//使用where约束

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1,_ anotherContainer: C2) -> Bool {
        
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        return true
}

var stackProOfStrings2 = StackPro<String>()
stackProOfStrings2.push("uno")
stackProOfStrings2.push("dos")
stackProOfStrings2.push("tres")

var arrayOfStrings = ["uno", "dos", "tres"]

//按这么绕了一大圈之后arrayOfStrings和stackProOfStrings2这两个类型不同的对象可以比较了
if allItemsMatch(stackProOfStrings2, arrayOfStrings) {
    print("All items match.")  //输出 All items match
} else {
    print("Not all items match.")
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容