每周 Swift 社區問答 2016-01-20

jopen 9年前發布 | 6K 次閱讀 Swift

作者: shanks

本周共整理了 5 個問題,程序員的問題也是千奇百怪。。大家可以看看本周整理的幾個問題。。好好感受下吧

本周整理問題如下:

  • Can i have 2 types on parameter in swift?
  • Swift: why aren’t all variables lazy by default?
  • Sorting Dictionarys in Arrays
  • Swift: Factory Pattern, Subclass methods are not visible
  • In Swift, how do I prevent a function from being called on a subclass?

對應的代碼都放到了 github 上,有興趣的同學可以下載下來研究: 點擊下載

Question1: Can i have 2 types on parameter in swift?

Q1鏈接地址

問題描述

樓主想根據傳入參數類型,來做不同的處理,見以下的偽代碼:

func (parameter: unknownType){
    if(typeof parameter == Int){
        //do this
    }else if(typeof parameter == String){
        //do that
    }
}

問題解答

初看這個問題,以為用泛型就可以解決。但是想到要根據類型做不同的處理,也就是說,需要判斷具體類型,然后做一些操作。而泛型跟類型無關。所以用泛型解決不了這個問題。讓我們來看看跟帖們的解決方案吧。

  • 方案一:使用重載
class Example
{
    func method(a : String) -> String {
        return a;
    }
    func method(a : UInt) -> String {
        return "{\(a)}"
    }
}

Example().method("Foo") // "Foo"
Example().method(123) // "{123}"
  • 方案二:使用 Any
func foo(bar: Any){
    switch(bar) {
        case let a as String:
            /* String類型的操作 */
            print("The bar is a String, bar = " + a)
        case let a as Int:
            /* Int 類型操作 */
            print("The bar is an Int, bar = \(a)")
        case _ : print("The bar is neither an Int nor a String, bar = \(bar)")
    }
}

/* Example */
var myString = "Hello"
var myInt = 1
var myDouble = 1.5

foo(myString) // The bar is a String, bar = Hello
foo(myInt)    // The bar is an Int, bar = 1
foo(myDouble) // The bar is neither an Int nor a String, bar = 1.5
  • 方案三:使用協議
 protocol MyProtocol {
    func doSomething()
}

extension Int: MyProtocol {
    func doSomething() {
        print("I am an int")
    }
}

extension String: MyProtocol {
    func doSomething() {
        print("I am a string")
    }
}

func doSomething(parameter: MyProtocol) {
    parameter.doSomething()
}


doSomething(1) // I am an int
doSomething("a") // I am a string
//doSomething(14.0) // 因為Double類型沒有擴展實現 MyProtocol 協議,所以報錯

Question2: Swift: why aren’t all variables lazy by default?

Q2鏈接地址

問題描述

樓主提問,為什么 lazy 關鍵字不能作為默認的存在。他列出了自己的分析:

先看看 2 行代碼,更直觀的進行比較:

var networkManager = NetworkManager.sharedInstance()

var lazy networkManager = NetworkManager.sharedInstance()

2行代碼共有的特性點:

  • 可以通過右值的計算得到值
  • 可以直接賦值

lazy 的好處:

  • 可以引用 self
  • 在使用時才計算得到值
  • 如果沒有使用它,就不會計算它

非 lazy 的好處:

由此分析可以得出結論:lazy 可以默認的定義存在。

問題解答

跟帖中發表了一些看法,值得參考:

  • lazy 增加了程序的不確定性,因為并知道,何時會用到 lazy 定義的變量。
  • lazy 使用場景是在需要得到一個計算量很大或者占用資源很大的變量的時候使用的,一般情況下,不需要用到 lazy
  • 在 Swift 函數式編程中,用到一些帶有 lazy 性質的特性,比如 map 和filter 就是后計算的。lazy 性質的特性在 Swift 用的比較多。但是 lazy 不是線程安全的。

Question3: Sorting Dictionarys in Arrays

Q3鏈接地址

問題描述

樓主的問題是,根據字典中某一個key進行排序,按照順序輸出結果,見下面代碼:PS:帖子中代碼是不能運行的,因為product1的類型編譯器推斷不出來,必須顯式指定為:[String: Any],跟帖中定義為[String: AnyObject] 也是錯的。因為value都是值類型的,不能用AnyObject來表示。

// 輸入
var product1: [String: Any] = ["name": "milk", "price": 3.2]
var product2: [String: Any] = ["name": "bread", "price": 2.9]
var product3: [String: Any] = ["name": "meat", "price": 4.1]
var product4: [String: Any] = ["name": "sweets", "price": 1.0]
// 輸出
var priceArray = [1.0,2.9,3.2,4.1]
var nameArray = ["sweets","bread","milk","meat"]

問題解答

  • 解法一:傳統的思路來解決
let product11: [String: Any] = ["name": "milk","price": 3.2]
let product21: [String: Any] = ["name": "bread","price": 2.9]
let product31: [String: Any] = ["name": "meat","price": 4.1]
let product41: [String: Any] = ["name": "sweets", "price": 1.0]

var tempDictArray = [[String: Any]]()
tempDictArray.append(product11)
tempDictArray.append(product21)
tempDictArray.append(product31)
tempDictArray.append(product41)

func priceSort(dict1: [String: Any], dict2: [String: Any]) -> Bool {
    let price1 = dict1["price"] as? Float ?? 
    let price2 = dict2["price"] as? Float ?? 
    return price1 < price2
}


tempDictArray = tempDictArray.sort(priceSort)

var priceArray1 = [Float]()
var nameArray1 = [String]()

for item in tempDictArray {

    let price = item["price"] as? Float ?? 
    let name = item["name"] as! String

    priceArray1.append(price)
    nameArray1.append(name)
}
priceArray1    //[1, 2.9, 3.2, 4.1]
nameArray1     //["sweets", "bread", "milk", "meat"]
  • 解法二:推薦解法

1、使用 struct 替代字典,更加直觀和自然

struct Product {
    let name: String
    let price: Double

    init?(dict:[String:Any]) {
        guard
            let name = dict["name"] as? String,
            let price = dict["price"] as? Double else {
                return nil
        }
        self.name = name
        self.price = price
    }
}

2、定義輸入

let productsDict = [product11, product21, product3, product4]

3、排序得到結構體數組

let products = productsDict.flatMap { Product(dict: $0) }.sort{ $0.price < $1.price }

4、輸出結果

let prices2 = products.map { $0.price }
let names2 = products.map { $0.name }
  • 解法三:與第二種大同小異,有一些細節的差異,大家可以參考一下:
// Define a structure to hold your data.
// The protocol CustomStringConvertible allows you to format the
// data as a nice string for printing.

struct Product1: CustomStringConvertible {
    var name: String
    var price: Double
    var description: String { return "name: \(name), price: \(price)" }

    init?(info: [String: Any]) {
        guard let name = info["name"] as? String,
            let price = info["price"] as? Double else { return nil }
        self.name = name
        self.price = price
    }
}


var products1 = [Product1(info: product1), Product1(info: product2), Product1(info: product3), Product1(info: product4)].flatMap {$0}

products1.sortInPlace { $0.price < $1.price }

products1.forEach { print($0) }

let priceArray2 = products1.map { $0.price }  // [1.0, 2.9, 3.2, 4.1]
let nameArray2 = products1.map { $0.name }    // ["sweets", "bread", "milk", "meat"]

Question4: Swift: Factory Pattern, Subclass methods are not visible

問題鏈接

Q4鏈接地址

問題描述

樓主想實現工廠模式,于是寫下以下代碼,報錯:

protocol IInterface {
    func InterfaceMethod() -> Void
}

public class SubClass: IInterface {
    init() {}

    func InterfaceMethod() { }

    public func SubClassMethod() { }
}

public class Factory {
    class func createFactory() -> IInterface {
        return SubClass()
    }
}

let mgr = Factory.createFactory()

mgr.InterfaceMethod()
//mgr.SubClassMethod() // 報錯

問題解答

樓主對工廠模式看來理解不深刻,跟帖中做了解釋,首先樓主實現的的簡單工廠模式,簡單工廠會根據對工廠類不同的調用產生最終的使用的類,見跟帖中的例子:

protocol Animal {
    func voice() -> String
}

class Dog: Animal {
    func voice() -> String {
        return "bark"
    }

    func sit() {
    }
}

class Cat: Animal {
    func voice() -> String {
        return "meow"
    }
}

class AnimalFactory {
    func getAnimal() -> Animal {
        return Dog()
    }
}


let animalFactory = AnimalFactory()
animalFactory.getAnimal()

稍微做一下改造,就可以根據傳入參數,產生不同的 Animal

enum AnimalType {
    case DogType
    case CatType
}

class AnimalFactory1 {
    func getAnimal(type: AnimalType) -> Animal {

        switch(type) {
            case .DogType:
                return Dog()
            case .CatType:
                return Cat()
        }
    }
}

let animalFactory1 = AnimalFactory1()
animalFactory1.getAnimal(AnimalType.CatType)

還有其他 2 種相關的模式:工廠方法模式和抽象工廠模式,具體的模式介紹,請參看 這里 , 大家有興趣可以自己用 swift 實現。歡迎跟帖!

Question5: In Swift, how do I prevent a function from being called on a subclass?

問題鏈接

Q5鏈接地址

問題描述

樓主的問題是,給了一個子類的實例,如何在編譯器層面,阻止子類去調用或者實現父類的方法。樓主說的應用場景是父類實現了 add 和 move 操作,子類通過另外一個方法 change ,調用了父類的 add 和 move 方法。這時候子類的實例,只需要調用 change 方法就可以了,沒必要調用 add 和 remove 了。

class Parent {

    func doThis() { print("Parent Did This") }
    func doThat() { print("Parent Did That") }

}

class Child: Parent {

    override func doThis() {
        print("Child Did This")
    }

    // I want this to be a compile time error
    override func doThat() {
        print("Not Supported")
        return
    }

}

問題解答

跟帖中提到了使用 @available 來聲明訪問的可用性,見代碼:

class Parent {
    func doThis() { print("Parent Did This") }
    func doThat() { print("Parent Did That") }
}

class Child: Parent {
    override func doThis() {
        print("Child Did This")
    }

    @available(*, unavailable, message="Child can't doThat")
    override func doThat() {
        print("Not Supported")
    }
}


let parent = Parent()
parent.doThis()
parent.doThat()

let child = Child()
child.doThis()
child.doThat() // 編譯報錯

問題思考

用父類子類來定義這個問題的結構是有問題的。可以另外定義沒有繼承關系一個類來包裹 add 和 remove 操作。即可以解決樓主的問題。當然如果真的需要用到繼承,也是可以的,用上面的方法即可。不過目前的趨勢都是少用繼承,多用 protocol 來解決問題。。

來自: http://swift.gg/2016/01/20/swift-qa-2016-01-20/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!