Как определить инициализаторы в расширении протокола?

protocol Car {
     var wheels : Int { get set}

     init(wheels: Int)

}

extension Car {
    init(wheels: Int) {
        self.wheels = wheels
    }
}

на self.wheels = колеса я получаю ошибку

Error: variable 'self' passed by reference before being initialized

Как определить инициализатор в расширении протокола?

Ответ 1

Как вы можете видеть, это не работает в этих условиях, потому что при компиляции необходимо убедиться, что все свойства инициализированы перед использованием struct/enum/class.

Вы можете сделать еще один инициализатор требованием, чтобы компилятор знал, что все свойства инициализированы:

protocol Car {
    var wheels : Int { get set }
    // make another initializer
    // (which you probably don't want to provide a default implementation)
    // a protocol requirement. Care about recursive initializer calls :)
    init()
    init(wheels: Int)

}

extension Car {
    // now you can provide a default implementation
    init(wheels: Int) {
        self.init()
        self.wheels = wheels
    }
}

// example usage

// mark as final
final class HoverCar: Car {
    var wheels = 0
    init() {}
}

let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4

Как и в Xcode 7.3 beta 1, он работает с structs, как ожидалось, но не с классами, поскольку, если они не являются final, init(wheels: Int) в протоколе является required init, и его можно переопределить, поэтому он не может быть добавляется через расширение. Обходной путь (как предлагает корреспондент): Сделайте class final.

Другое обходное решение (в глубине, без final class)

Чтобы работать с классами, не делая их окончательными, вы также можете отказаться от требования init(wheels: Int) в протоколе. Кажется, что он ведет себя не иначе, как прежде, но рассмотрит этот код:

protocol Car {
    var wheels : Int { get set }
    init()
    // there is no   init(wheels: Int)
}

extension Car {
    init(wheels: Int) {
        self.init()
        print("Extension")
        self.wheels = wheels
    }
}

class HoverCar: Car {
    var wheels = 0
    required init() {}
    init(wheels: Int) {
        print("HoverCar")
        self.wheels = wheels
    }
}

// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)

func makeNewCarFromCar<T: Car>(car: T) -> T {
    return T(wheels: car.wheels)
}

// prints "Extension"
makeNewCarFromCar(drivableHoverCar)

Итак, если вы делаете Car из общего контекста, где тип, на который вы вызываете init, должен быть известен только как Car, инициализатор расширения вызывается, даже если инициализатор определен в HoverCar. Это происходит только потому, что в протоколе нет требования init(wheels: Int).

Если вы добавите его, у вас есть прежняя проблема с объявлением class как final, но теперь он печатает два раза "HoverCar". В любом случае, вторая проблема, вероятно, никогда не возникает, поэтому это может быть лучшим решением.

Sidenote: Если я допустил некоторые ошибки (код, язык, грамматика,...), вы можете исправить меня:)

Ответ 2

@Qbyte верен.

Кроме того, вы можете взглянуть на мой настраиваемый

В том, что у меня есть Initable protocol

public protocol Initable {
    // To make init in protocol extension work
    init()
}

public extension Initable {
    public init(@noescape block: Self -> Void) {
        self.init()
        block(self)
    }
}

Затем, чтобы соответствовать ему

extension Robot: Initable { }

У меня есть два способа: использовать final или реализовать init

final class Robot {
    var name: String?
    var cute = false
}

class Robot {
    var name: String?
    var cute = false

    required init() {

    }
}

Ответ 3

Я понимаю, что это невозможно, поскольку расширение протокола не может знать, какие свойства имеет соответствующий класс или структура, и поэтому не может гарантировать, что они правильно инициализированы.

Если есть способы обойти это, мне очень интересно знать!:)

Ответ 4

Может быть, не то же самое, но в моем случае вместо использования init я использовал статический func для возврата объекта класса.

protocol Serializable {
   static func object(fromJSON json:JSON) -> AnyObject?
}

class User {
    let name:String

    init(name:String) {
        self.name = name
    }
}

extension User:Serializable {

    static func object(fromJSON json:JSON) -> AnyObject? {
        guard let name = json["name"] else {
            return nil
        }

        return User(name:name)
     }

}

Затем для создания объекта я делаю что-то вроде:

let user = User.object(fromJSON:json) as? User

Я знаю, что это не лучшее, что когда-либо было, но это лучшее решение, которое я мог найти, чтобы не сочетать бизнес-модель с уровнем данных.

ПРИМЕЧАНИЕ. Я ленив, и я закодировал все непосредственно в комментарии, поэтому, если что-то не работает, дайте мне знать.