作者: shanks
大家新年好,每周问答整理又来了。这个栏目会一直做下去,目前的表达形式也在想做一些改变,更加方便大家阅读,不过还没想好具体怎么做,大家有啥意见,都可以提出来哈。本周共整理了 5 个问题。涉及问题有:泛型输出类型问题,数组的contains方法问题,数组的元素合并问题,枚举原始值的默认值问题,字典的扩展问题
本周整理问题如下:
对应的代码都放到了 github 上,有兴趣的同学可以下载下来研究: 点击下载
Q1链接地址
这个问题有点绕,大家细细看代码,涉及到继承和泛型的知识点。楼主的问题是问,定义的 serviceC
中的属性 cache
的 dynamicType
为神马是 AbstractCache<B>
,而不是 AbstractCache<C>
。
// Models:
class A {}
class B: A { }
class C: B { }
// Cache:
class AbstractCache<T> {}
// Services:
class AbstractService<T> {
let cache = AbstractCache<T>()
}
class ServiceA<T: A>: AbstractService<T> {}
class ServiceB<T: B>: ServiceA<B> {}
class ServiceC<T: C>: ServiceB<C> {}
let serviceC = ServiceC()
print(serviceC.cache.dynamicType) // 这里输出AbstractCache<B>
楼主首先定义了类A, B, C 的继承关系。然后定义了一个泛型类 AbstractCache<T>
,随后又定义了一个使用 AbstractCache<T>
的泛型类 AbstractService<T>
, 里面有一个属性 cache
是 AbstractCache<T>
的对象。这个时候 T 还没指定具体的类型。第一次指定 T 类型的时候是在定义 ServiceB<T: B>
时候,继承自 ServiceA<B>
,这里的 T 替换成了 B,所以即使 ServiceC<T: C>
继承自 ServiceB<C>
, 因为 B,C是继承关系,这样定义是合法的。但是改变不了 T 的指代。所以 cache
的 dynamicType
为 AbstractCache<B>
。
要解决这个问题,见以下代码,定义继承的时候,不要指定泛型具体的类型,见以下代码:
class ServiceA<T: A>: AbstractService<T> {}
class ServiceB<T: B>: ServiceA<T> {}
class ServiceC<T: C>: ServiceB<T> {}
let serviceC = ServiceC()
print(serviceC.cache.dynamicType) // 这里输出AbstractCache<C>
Q2链接地址
当数组的元素类型是自定义的类时,使用contains方法会报错:
var array = ["A", "B", "C"]
array.contains("A") // 正确
class Dog {
var age = 1
}
var dogs = [Dog(), Dog(), Dog()]
var sparky = Dog()
//dogs.contains(sparky) //报错
let result = dogs.contains({ $0 === sparky })
数组的contains方法需要用到 ==
操作符,所以需要自定义的类实现 Equatable
协议即可:
class Dog: Equatable {
var age = 1
}
func == (lhs: Dog, rhs: Dog) -> Bool {
return lhs.age == rhs.age
}
继续看看深度的解释:Array 中 contains 有 2 个定义:
Generator.Element 存在 Generator.Element : Equatable
的约束,要求元素实现 Equatable
协议。Dog没有实现此协议的话,那么就不能匹配这个方法。
此方法定义了一个闭包来实现比较,在 Dog 没有实现 Equatable
协议的前提下,我们可以这样对Dog进行比较:
let result = dogs.contains({ $0 === sparky })
这样做只有比较2个对象的引用是否一样,而不是比较对象的值是否相等。所以不满足需求。
所以,最终的方案,还是实现 Equatable
协议吧。
Q3链接地址
楼主问题是:给定一个数组,输出一个二维数组,连续相同的数组组成子数组。
比如:
输入:[7, 7, 3, 2, 2, 2, 1, 7, 5, 5]
输出:[[7, 7], [3], [2, 2, 2], [1], [7], [5, 5]]
楼主给出了常规的实现(不过跑不起来,要改一下实现),见以下代码:
let mainArray = [7, 7, 3, 2, 2, 2, 1, 7, 5, 5]
var oldNum = mainArray[0]
var array = [[Int]]()
for var i = 0; i < mainArray.count; i++ {
var columnArray = Array<Int>()
for var j in 0...9 {
if oldNum == mainArray[j]{
columnArray.append(mainArray[j])
}
else {
array.append(columnArray)
//j += 1
break;
}
oldNum = mainArray[j];
j += 1
}
}
array //输出错误的结果
正确的常规做法应该是:
array = [[Int]]()
var columnArray = Array<Int>()
for var i = 0; i < mainArray.count; i++ {
if oldNum == mainArray[i]{
columnArray.append(oldNum)
} else {
array.append(columnArray)
columnArray = Array<Int>()
oldNum = mainArray[i]
i--
}
}
array.append(columnArray) // 补齐最后的部分
来看看如何用函数式编程来解决这个问题吧:
let result = mainArray.reduce([[Int]]()) { (var result, num) -> [[Int]] in
if var lastSequence = result.last where lastSequence.first == num {
result[result.count-1].append(num)
} else {
result.append([num])
}
return result
}
result
Q4链接地址
吐槽一下,帖子标题的syntax单词写错了。这个问题是关于枚举类型的默认值的,楼主是想知道, Swift 中的枚举类型的原始值默认值有那些规则?
Swift 的枚举类型,如果指定了原始值的类型(Int, String等),就会有默认值的存在。如果不指定原始值类型,编译器就不会分配默认值。当枚举类型的原始值类型指定为 Int 时,这个时候,就和其他语言的枚举的默认值分配一样。比如下面的例子,Bar,Baz, Boz 的原始值就是 0, 1, 2
enum Foo: Int {
case Bar, Baz, Boz
}
Foo.Boz.rawValue // 原始值为2
如果指定了一个枚举值的原始值,后面接下来的枚举值的原始值会自动 +1, 比如:
enum Foo1: Int {
case Bar = 2, Baz, Boz // Baz 原始值为 3, Boz 原始值为 4
}
Foo1.Boz.rawValue
原始值类型为字符串的时候,默认的值就是和枚举值的字符串一样,比如:
enum Foo2: String {
case Bar, Baz, Boz
}
Foo2.Bar.rawValue // 原始值为 Bar
可以显示设置原始值,那么没有设置原始值的,就使用默认值
enum Foo3: String {
case Bar = "Something"
case Baz = "Something else"
case Boz // 隐式设置为 "Boz"
}
Foo2.Boz.rawValue
Q5链接地址
楼主对 Dictionary 做了一个扩展,报错了。
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
mutating func auth() -> Dictionary {
self.updateValue("2.0", forKey: "api") //Cannot invoke 'updateValue' with an argument list of type '(String, forKey:String)'
return self
}
}
extension Dictionary {
mutating func auth() {
updateValue("2.0" as! Value, forKey: "api" as! Key)
}
}
var dic: [String:AnyObject] = [:]
print(dic) // "[:]/n"
dic.auth()
print(dic) // "["api": 2.0]/n"