.sort in protocol extension is not working

泄露秘密 提交于 2019-12-11 04:45:36

问题


I have a protocol as well as a protocol extension and I'd like to implement a function in the protocol extension to sort an array defined by the protocol with custom objects, but it's not working.

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

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

Xcode is telling me that 'sort' has been renamed to 'sorted(by:)', but if im using this a new array gets created, but I need the old array to be sorted, not a new one.

What am I doing wrong?


回答1:


It's a misleading error – the problem is that you need to mark your sortArrayByCreationTime() method as mutating in order to tell the compiler that it's mutating a property (as protocols can be adopted by both value and reference types):

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



回答2:


I created a Minimal, Complete, and Verifiable example (MCVE) out of the original code, and I made it work in a Swift 3 playground

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) })

Let me give two options:

Option 1: Improved

In Option 1, extend the Array struct, so that you can create mutating method sortByCreationDate. There is a trick that needs to be pulled out of a hat in order to extend [MyObject] (an array of 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) })

Option 2: Better

In Option 2, extend Sequence. A sequence will allow you to sort objects that are Arrays, and other types, e.g., Dictionaries. Better yet, it does not create a mutating method. But honestly MyProtocol is a confusing API. With this method, MyProtocol is no longer necessary.

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) })


来源:https://stackoverflow.com/questions/40811214/sort-in-protocol-extension-is-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!