2016-11-25 67 views
1

我有一個協議以及一個協議擴展,我想在協議擴展中實現一個函數來將協議定義的數組與自定義對象排序,但它不起作用。。協議擴展中的排序不起作用

protocol MyProtocol { 
    var myArray: [MyObject] { get set } 
} 

extension MyProtocol { 
    func sortArrayByCreationTime() { 
     myArray.sort { 
      $0.created > $1.created 
     } 
    } 
} 

的Xcode告訴我,「排序」已更名爲「排序(由:)」,但如果IM使用這個新的數組被創建,但我需要舊的陣列進行排序,而不是新的一個。

我在做什麼錯?

+0

你的代碼永遠不會編譯,因爲它保證編譯器有一個數組元素具有'created'屬性嗎? – matt

+2

@matt顯然編譯器可以從'MyObject'類型推斷'created'屬性。 – vadian

+0

@vadian我的觀點是OP應該顯示MyObject作爲問題的一部分。 MCVE需要足夠的代碼進行編譯。 – matt

回答

1

這是一個誤導性的錯誤 - 問題是,你需要標記您的sortArrayByCreationTime()方法mutating爲了告訴大家,它的變異屬性(如協議可由兩個值類型和引用類型通過)編譯器:

extension MyProtocol { 
    mutating func sortArrayByCreationTime() { 
     myArray.sort { 
      $0.created > $1.created 
     } 
    } 
} 
+0

謝謝。如果我這樣做,我還需要做什麼才能在實現該協議的類中使用此函數,因爲現在我收到此錯誤:'不能在不可變值中使用變異成員:'self'是不可變的' – eLwoodianer

+0

@eLwoodianer很難知道沒有看到你怎麼稱呼它 - 我會建議用導致錯誤的代碼[mcve]發佈一個新問題(注意上面的matt的註釋 - 雖然它不是部分在這個特定問題中,如果你問關於你的代碼有問題的問題,你應該總是發佈一個MCVE。) – Hamish

+0

@eLwoodianer如果實現協議的類的實例是用' let'。如果我將我的類實例創建爲'var',我可以在其上調用'sortArrayByCreationTime'。 –

0

我創建了一個最小的,完整的,並且可驗證的例子(MCVE)出來的原代碼,和我做了它在斯威夫特3遊樂場工作

import UIKit 
import XCTest 

extension Date 
{ 
    init?(year: Int, month: Int, day: Int) { 
     var dateComponents = DateComponents() 
     dateComponents.day = day 
     dateComponents.month = month 
     dateComponents.year = year 
     guard let date = Calendar.current.date(from: dateComponents) 
     else { return nil } 
     self = date 
    } 
} 

struct MyObject { 
    var created: Date 
} 

protocol MyProtocol { 
    var myArray: [MyObject] { get set } 
} 

extension MyProtocol { 
    mutating func sortArrayByCreationTime() { 
     myArray.sort { 
      $0.created > $1.created 
     } 
    } 
} 

struct ArrayContainer: MyProtocol { 
    var myArray: [MyObject] 
} 

let objects = [ 
    MyObject(created: Date(year: 2016, month: 9, day: 1)!), 
    MyObject(created: Date(year: 2016, month: 11, day: 1)!), 
    MyObject(created: Date(year: 2016, month: 4, day: 1)!), 
    MyObject(created: Date(year: 2016, month: 8, day: 1)!), 
] 
var container = ArrayContainer(myArray: objects) 

var dateFormatter = DateFormatter() 
dateFormatter.dateFormat = "MMdd" 

XCTAssertEqual(["0901", "1101", "0401", "0801"], 
       container.myArray.map { dateFormatter.string(from: $0.created) }) 
container.sortArrayByCreationTime() 
XCTAssertEqual(["1101", "0901", "0801", "0401"], 
       container.myArray.map { dateFormatter.string(from: $0.created) }) 

我舉兩個選擇:

選項1:改進

在選項1中,擴展Array結構,以便您可以創建變異方法sortByCreationDate。存在需要被拉出一個帽子以延長[MyObject來](爲MyObject的陣列)

protocol MyObjectProtocol { var created: Date { get set } } 
extension MyObject: MyObjectProtocol { } 
extension Array where Element: MyObjectProtocol { 
    mutating func sortByCreationTime() { 
     self.sort { 
      $0.created > $1.created 
     } 
    } 
} 

var container2 = ArrayContainer(myArray: objects) 
XCTAssertEqual(["0901", "1101", "0401", "0801"], 
       container2.myArray.map { dateFormatter.string(from: $0.created) }) 
container2.myArray.sortByCreationTime() 
XCTAssertEqual(["1101", "0901", "0801", "0401"], 
       container2.myArray.map { dateFormatter.string(from: $0.created) }) 

選項2特技:更好

在選項2,延伸序列。一個序列將允許您排序數組的對象和其他類型的對象,例如字典。更好的是,它不會創建變異方法。但老實說MyProtocol是一個令人困惑的API。用這種方法,MyProtocol不再是必需的。

extension Sequence where Iterator.Element == MyObject { 
    func sortedByCreationTime() -> [Iterator.Element] { 
     return self.sorted { 
      $0.created > $1.created 
     } 
    } 
} 
var container3 = ArrayContainer(myArray: objects) 
XCTAssertEqual(["0901", "1101", "0401", "0801"], 
       container3.myArray.map { dateFormatter.string(from: $0.created) }) 
XCTAssertEqual(["1101", "0901", "0801", "0401"], 
       container3.myArray.sortedByCreationTime().map { dateFormatter.string(from: $0.created) })