作者: shanks
本周大事是第一届中国 Swift 开发者大会。笔者也请假去北京朝圣。由于大神超多,场面很火爆。可以看出随着 Swift 的开源,加入 Swift 大军的程序猿越来越多。大神们的讲座也很精彩。不虚此行。大家可以通过 这里 查看大会的精彩内容。本周共整理了5个问题。
本周整理问题如下:
对应的代码都放到了 github 上,有兴趣的同学可以下载下来研究: 点击下载
Q1链接地址
国外的程序员比较较真,如果逻辑上说得通的表达,就要问清楚是不是这么回事,楼主的问题是:
官网上有一句话写的是,Swift 出现的目的是为了取代那些基于C语言的系列语言(C,C++,OC)。
而 Swift 编译器是使用 C++ 开发出来的,那么是不是意味着,有一天 Swift 编译器可以用 Swift 来重新实现一遍?
在 swift-evolution 刚好有一个proposal 提到了这一点,被否了。原因是 LLVM 也是基于 C++ 实现的,除非重写 LLVM,然后用 Swift 把 C++ 的 API 重新实现一遍。做这个还不如做一些优先级高的事情。
Q2链接地址
楼主想要实现的功能是,定义一个类的构造函数,传入一个字典,使用字典里面的值,赋值给类中的成员变量。但是在赋值语句处报错。
class Product {
var name : String!
var type: String!
var description: String!
var taste: String!
var picturePath: String!
var pairings: [String]
var similar: [String]
init(dict: Dictionary<String, AnyObject>) {
let props = ["name", "type", "description", "taste", "pairings", "similar"]
for prop in props {
self[prop] = dict[prop] //报错:Type 'Product' has no subscript members
}
}
}
楼下的解答,也比较巧妙,类需要继承 NSObject,也就具有了 OC 的一些特性。在构造函数中使用 Mirror 得到类的结构信息,进一步得到类的成员变量数组,同时使用 oc 的 setValue(,forKey:) 设置对应的成员变量:
class Product : NSObject {
var varDump: AnyObject?
var name : String?
var type: String?
// ...
init(dict: Dictionary<String, AnyObject>) {
super.init()
let a = Mirror(reflecting: self).children.filter { $0.label != nil }
for b in a {
self.setValue(dict[(b.label ?? "")], forKey: (b.label ?? "varDump"))
}
}
}
var propertyInit = [String:AnyObject]()
propertyInit["name"] = "John"
propertyInit["type"] = "Human"
var a = Product(dict: propertyInit)
print(a.name ?? "Not initalized") // John
print(a.type ?? "Not initalized") // Human
Q3链接地址
楼主的问题是:
如何理解 struct 中的静态变量(static)?
1、在下面的代码中, Baz 类的构造函数只被调用了一次?
2、Foo 结构体中的静态常量 bar 是延迟加载的?
class Baz {
init(){
print("initializing a Baz")
}
}
struct Foo {
static let bar = Baz()
}
var z = Foo.bar
z = Foo.bar
静态变量(static)本身就是独立于对象存在的。通过类型名来直接访问。且在调用时候只初始化一次,也就是延迟加载的。
由于结构体是值类型,所以可以通过static关键字很方便的实现单例模式。
如果需要绑定到对象,不需要static即可。
struct Foo1 {
static let cache = NSCache()
// now you might have methods that use this shared `cache`
}
struct Foo2 {
let bar = Baz()
}
let x = Foo2()
let y = Foo2() //x,y 分别调用了 Baz 的构造函数
Q4链接地址
以下代码会在方法 addNewType 中报错,大家可以不看答案先,想想为啥会报错。这个问题比较初级,但是经常会有人问到。
class TypeA: NSObject {
override init() {
print("typeA")
}
}
class ObjectA<T: TypeA>: NSObject {
var type = [T]()
init(type:T) {
self.type.append(type)
}
func addNewType() {
let newType = TypeA()
self.type.append(newType) // Cannot invoke "append" with an argument list of type '(TypeA)'
}
}
ObjectA 类定义的泛型 T 遵从类 TypeA,定义的数组是[T],进行append操作时候,不能匹配具体的 TypeA,所以报错。可以把 type 换成 [TypeA], 因为泛型 T 是表示 TypeA 或者 它的子类。所以[TypeA] 可以 append T 或者 TypeA:
class ObjectA1<T: TypeA>: NSObject {
var type = [TypeA]()
init(type:T) {
self.type.append(type)
}
func addNewType() {
let newType = TypeA()
self.type.append(newType) // Cannot invoke "append" with an argument list of type '(TypeA)'
}
}
Q5链接地址
以下代码会报错,String 不提供直接的下标访问字符串里面的字符了。这对于 C 和 C++ 程序员来说,有点不太适应。
var string = "Hello, world!"
var firstChar:Character = string[0] // Throws error
需要自定义subscript来提供对字符串的访问,内部是通过Range来提供范围进行访问,见以下代码:
解法1:扩展 SequenceType,因为数组的遍历是基于 SequenceType 协议的,所以自然会拥有扩展的方法 frequencies,用来计算元素在数组出现的次数。然后通过 sort 方法输出比较的结果。注意结果数组元素是元组形式。如果想变成与输入相同的结构,要做一下遍历输出。
extension String {
subscript (i: Int) -> Character {
return self[self.startIndex.advancedBy(i)]
}
subscript (i: Int) -> String {
return String(self[i] as Character)
}
subscript (r: Range<Int>) -> String {
let start = startIndex.advancedBy(r.startIndex)
let end = start.advancedBy(r.endIndex - r.startIndex)
return self[Range(start: start, end: end)]
}
}
string.characters.first
关于 Swift 2 中对字符串更多的解释,可以查看: https://developer.apple.com/swift/blog/?id=30