协议扩展中的.sort不起作用

时间:2016-11-25 19:20:37

标签: swift sorting swift3 swift-protocols swift-extensions

我有一个协议以及一个协议扩展,我想在协议扩展中实现一个函数,用自定义对象对协议定义的数组进行排序,但它不起作用。

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

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

Xcode告诉我'sort'已经被重命名为'sorted(by :)',但是如果我正在使用它,则会创建一个新数组,但我需要对旧数组进行排序,而不是新数组。 / p>

我做错了什么?

2 个答案:

答案 0 :(得分:1)

这是一个误导性错误 - 问题是您需要将sortArrayByCreationTime()方法标记为mutating,以告诉编译器它正在改变属性(因为协议可以被值和参考类型):

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

答案 1 :(得分:0)

我从原始代码中创建了一个Minimal,Complete和Verifiable示例(MCVE),并使其在Swift 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) })