Требование протокола и класса в быстрых свойствах

В Objective-C для свойств может потребоваться реализация классов и дополнительных протоколов:

@property (nonatomic) UIViewController<UISplitViewDelegate> *viewController;

Возможно ли это в Swift? Из документации похоже, что вам может потребоваться только класс или протокол.

Ответы

Ответ 1

В Swift 4 вы можете сделать это:

let viewController: UIViewController & UISplitViewDelegate

Ответ 2

В Swift существует два способа достижения этой цели:

  • Использование пустого протокола < phantom. Создайте пустой протокол и совместите с ним UIViewController. Это самый "быстрый" метод, он безопасный и динамический (не требует указания класса во время компиляции).

    protocol _UIViewControllerType {}
    extension UIViewController: _UIViewControllerType {}
    
    class MyClass {
        weak var viewController: protocol<UISplitViewControllerDelegate, _UIViewControllerType>?
    }
    

    Вы также можете объявить typealias для этого типа (просто чтобы уменьшить беспорядок кода).

    class MyClass {
        typealias ViewControllerType = protocol<UISplitViewControllerDelegate, _UIViewControllerType>
        weak var viewController: ViewControllerType?
    }
    
  • Использование общих ограничений. Как упоминалось fnc12 и Константин Коваль. Это безопасно, но не позволяет вам "заменять" экземпляр контроллера представления во время выполнения.

    class MyClass<T: UIViewController where T: UISplitViewControllerDelegate> {
        weak var viewController: T?
    }
    

Я надеюсь, что следующий выпуск Swift добавит способ указать оба ограничения без использования протокола phantom...

typealias ViewControllerType = UIViewController: UISplitViewControllerDelegate // wish

Ответ 3

Да, вы можете это сделать

class A < T : SomeClass where T: Comparable> {
    var myProperty: T
    init(t :T) {
        myProperty = t
    }
}

Объявить класс A, который имеет свойство типа T. T является SomeClass или подклассом, и он должен принять протокол Comparable

При объявлении свойства вы можете использовать протокол типа

class MyClass {
    var nsobject: NSObjectProtocol
    init(object : NSObjectProtocol) {
        nsobject = object
    }
}

// Pure Swift
protocol RandomNumberGenerator {
}

class Dice {
    let generator: RandomNumberGenerator
    //specify many protocols
    let printer: protocol<Printable, NicePrintable> 
}

Вы можете прочитать здесь

Ответ 4

То же, что и @akashivskyy, используя пустой протокол phantom
Но здесь я делаю это как отдельный класс, который реализует это protocol - MyViewController, который может использоваться как тип для var декларация. Что упрощено в моей реализации.

@objc protocol MySplitViewControllerDelegate : NSObjectProtocol {
    func controllerTitle() -> String
    optional func mySplitView() // write delegates
}

class MyViewController: UIViewController, MySplitViewControllerDelegate {
    func controllerTitle() -> String {
        return ""
    }
}

class ViewController: UIViewController {

    private(set) weak var viewController: MyViewController?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Ответ 5

Здесь нужны дженерики.

@property (nonatomic) UIViewController<UISplitViewDelegate> *viewController;

Предположим, что класс с именем MyClass имеет свойство, называемое viewController, с типом UIViewController (или подкласса) и которое соответствует протоколу UISplitViewDelegate. В Swift ваш код будет выглядеть как

class MyClass<T:UIViewController where T:UISplitViewControllerDelegate>:NSObject{
    var viewController:T?

    override init(){
        super.init()
        //..
    }

    //  etc..
}

Примечание

class MyClass<T:UIViewController where T:UISplitViewControllerDelegate>:NSObject

линии. Здесь вы указываете случайный тип T, но вы также указываете, что вы хотите получить T из UIViewController и соответствовать UISplitViewControllerDelegate. Это условие будет проверено во время компиляции. И вы объявляете свойство в этой строке

var viewController:T?

и укажите его тип как T. И остался один вопрос - как объявить переменную типа MyClass? Я предоставляю минимальный код из примера проекта, чтобы проиллюстрировать его более четко.

class MyClass<T:UIViewController where T:UISplitViewControllerDelegate>:NSObject{
    var viewController:T?

    override init(){
        super.init()
        //..
    }

    //  etc..
}

class ViewController: UIViewController,UISplitViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        var a=MyClass <ViewController> ()
        a.viewController=self
        //..
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //..
}

Дополнительная информация здесь

Удачи вам в дженериках.