2016-11-25 3 views
1

У меня есть протокол, а также расширение протокола, и я хотел бы реализовать функцию в расширении протокола для сортировки массива, определенного протоколом с пользовательскими объектами, но он не работает ,.sort в расширении протокола не работает

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

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

Xcode говорит мне, что «своего рода» была переименована в "отсортирован (по :), но если им, используя этот новый массив будет создаваться, но мне нужен старый массив для сортировки, а не новый.

Что я делаю неправильно?

+0

Ваш код никогда не собирается компилироваться в его нынешнем виде, потому что какая гарантия имеет компилятор, что элемент массива имеет свойство 'created'? – matt

+2

@matt По-видимому, компилятор может вывести свойство 'created' из' MyObject'. – vadian

+0

@vadian Моя точка зрения заключается в том, что OP должен был показать MyObject как часть вопроса. Для MCVE требуется достаточно кода для компиляции. – matt

ответ

1

Это заблуждение, ошибка - проблема в том, что вам нужно, чтобы отметить свой метод sortArrayByCreationTime() как mutating для того, чтобы сообщить компилятору, что он мутирует свойство (как протоколы могут быть приняты как ценности и ссылочных типов):

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

Спасибо. Если я это сделаю, что еще мне нужно сделать, чтобы использовать эту функцию в классе, реализующем протокол, потому что теперь я получаю эту ошибку: «Нельзя использовать мутирующий элемент в неизменяемом значении:« я »неизменен» – eLwoodianer

+0

@eLwoodianer Трудно узнать, не видя, как вы пытаетесь это назвать - я бы посоветовал опубликовать новый вопрос с [mcve] кода, вызывающего ошибку (обратите внимание на комментарий мата выше - хотя это не было частью проблемы в этом конкретном вопросе, вы всегда должны выставлять MCVE, если вы задаете вопрос о проблеме с вашим кодом.) – Hamish

+0

@eLwoodianer Я получил ту же ошибку, если экземпляр класса, реализующего протокол, был создан с использованием ' let'. Если бы я создал экземпляр класса как «var», я мог бы называть его 'sortArrayByCreationTime'. –

0

Я создал Minimal, полный и проверяемый пример (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, расширить массив-структуру, так что вы можете создать метод 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) })