Функция Mutating в расширении протокола, где Self - UIViewController

Я написал протокол и соответствующее расширение, которое использует простое StringStack в тандеме с соглашением об именах в форме "<origin>@<destination>" выполнять переходы между вложенными представлениями в моем приложении Swift. Я новичок в swift, так что если есть лучший способ реализовать то, что по сути является кнопками "вперед" и "назад" в нескольких представлениях, я открыт для этого. Однако следующий код проблематичен по причинам, которые я подробно опишу ниже.

struct StringStack {
    var stack: Array = [String]()

    mutating func push(str: String)->Void {
        self.stack.append(str)
    }

    mutating func pop()->String {
        self.stack.popLast()
    }
}

protocol SegueForwardAndBackward {
    var thisIdentifier: String {get set}
    var segueStack: StringStack {get set}
}

extension SegueForwardAndBackward where Self: UIViewController {

    mutating func performForwardSegue(nextIdentifier: String) {
        let identifier: String = thisIdentifier + "@" + nextIdentifier
        segueStack.push(identifier)
        performSegueWithIdentifier(identifier, sender: self)
    }

    mutating func performBackwardSegue() {
        let divided = segueStack.pop().componentsSeparatedByString("@")
        // reverses destination and origin of identifier
        let segueIdentifier: String = divided[1] + "@" + divided[0]
        performSegueWithIdentifier(segueIdentifier, sender: self)
    }

    func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if var destinationViewController: SegueForwardAndBackward = segue.destinationViewController as? SegueForwardAndBackward {
            destinationViewController.segueStack = segueStack
        }
    }
}

Моя первоначальная проблема заключается в том, что функции расширения не допускаются в расширении протокола, как подробно описано в этом отчете об ошибках. В этом отчете предложен обходной путь, однако приведенные ниже ошибки приводят к тому, что 'SegueForwardAndBackward' is not a subtype of 'UIViewController',

class A: UIViewController, SegueForwardAndBackward {

    mutating func testFunc() {
        var p: SegueForwardAndBackward = self
        p.performBackwardSegue()
    }
}

Я наткнулся на потенциальное решение, где я мог бы сделать SegueForwardAndBackward протокол класса, как подробно описано в другом вопросе. Однако реализация протокола класса приводит к segfault, который я предполагаю, потому что я объявляю свое расширение where Self: UIViewController и, к сожалению, я понятия не имею, как решить эту проблему.

На данный момент я весьма разочарован тем, что кажется еще одной ошибкой в ​​Swift, поэтому любая помощь в переработке моего подхода или расшифровке этих ошибок будет высоко ценится.

Заранее спасибо за ваш ответ!

2 ответа

Решение

Похоже, что этот вопрос касается альтернативного решения для вложенных кнопок "вперед" и "назад", поэтому без каких-либо других ответов это то, с чем я буду работать. Однако проблема, которую я поставил, все еще выделяется как довольно странная ошибка в swift.

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

      struct StringStack {
    var stack: Array = [String]()

    mutating func push(str: String)->Void {
        self.stack.append(str)
    }

    mutating func pop()->String? {
        self.stack.popLast()
    }
}

protocol SegueForwardAndBackward {
    var thisIdentifier: String {get set}
    var segueStack: StringStack {get nonmutating set}
}

extension SegueForwardAndBackward where Self: UIViewController {

    func performForwardSegue(nextIdentifier: String) {
        let identifier: String = thisIdentifier + "@" + nextIdentifier
        segueStack.push(str: identifier)
        performSegue(withIdentifier: identifier, sender: self)
    }

    func performBackwardSegue() {
        guard let divided = segueStack.pop()?.components(separatedBy: "@") else { return }
        // reverses destination and origin of identifier
        let segueIdentifier: String = divided[1] + "@" + divided[0]
        performSegue(withIdentifier: segueIdentifier, sender: self)
    }

    func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if var destinationViewController: SegueForwardAndBackward = segue.destination as? SegueForwardAndBackward {
            destinationViewController.segueStack = segueStack
        }
    }
}

И ваш класс будет выглядеть

      class A: UIViewController, SegueForwardAndBackward {
    
    var thisIdentifier: String
    var segueStack: StringStack
    
    func testFunc() {
        self.performBackwardSegue
    }
    
    init(thisIdentifier: String, segueStack: StringStack) {
        super.init(nibName: "A", bundle: .main)

        self.thisIdentifier = thisIdentifier
        self.segueStack = segueStack
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Однако это означает, что если вы согласуете структуру с SegueForwardAndBackward, вам потребуется реализовать неизменяемый набор.

      struct B: SegueForwardAndBackward {
    
    var thisIdentifier: String
    var segueStack: StringStack { //Toy example
        get {
            UserDefaults.standard.object(forKey: "someKey") as! StringStack
        }
        nonmutating set {
            UserDefaults.standard.set(newValue, forKey: "someKey")
        }
    }
}
Другие вопросы по тегам