作者:shanks
本週大事是第一屆中國 Swift 開發者大會。筆者也請假去北京朝聖。由於大神超多,場面很火爆。可以看出隨著 Swift 的開源,加入 Swift 大軍的程式猿越來越多。大神們的講座也很精彩。不虛此行。大家可以通過這裡檢視大會的精彩內容。本週共整理了5個問題。
本週整理問題如下:
對應的程式碼都放到了 github 上,有興趣的同學可以下載下來研究:點選下載
<!--more-->
Question1: Can we expect that one day Swift Compiler will be written in Swift?
問題描述
國外的程式設計師比較較真,如果邏輯上說得通的表達,就要問清楚是不是這麼回事,樓主的問題是:
官網上有一句話寫的是,Swift 出現的目的是為了取代那些基於C語言的系列語言(C,C++,OC)。
而 Swift 編譯器是使用 C++ 開發出來的,那麼是不是意味著,有一天 Swift 編譯器可以用 Swift 來重新實現一遍?
問題解答
在swift-evolution剛好有一個proposal 提到了這一點,被否了。原因是 LLVM 也是基於 C++ 實現的,除非重寫 LLVM,然後用 Swift 把 C++ 的 API 重新實現一遍。做這個還不如做一些優先順序高的事情。
Question2: Type My Custom Class has no subscript members
問題描述
樓主想要實現的功能是,定義一個類的建構函式,傳入一個字典,使用字典裡面的值,賦值給類中的成員變數。但是在賦值語句處報錯。
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
Question3: Implicitly lazy static members in Swift
問題描述
樓主的問題是:
如何理解 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 的建構函式
Question4: How to use Generic type in Swift2
問題連結
問題描述
以下程式碼會在方法 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)'
}
}
Question5: Get nth character of a string in Swift programming language
問題連結
問題描述
以下程式碼會報錯,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