Как передать протокол в качестве параметра в Swift

17

В Objective-C я знаю, как передать protocol в качестве параметра:

- (void)MyMethod:(Protocol *)myparameter

Но в Swift больше нет типа Protocol .

Как я могу передать протокол в качестве параметра, не зная, что это такое?

    
задан Jean Lebrument 04.06.2014 в 17:14
источник
  • Передача протокола или передача объекта, который соответствует одному? –  Rui Peres 04.06.2014 в 17:59
  • Передача протокола –  Jean Lebrument 04.06.2014 в 18:46
  • Существует тип протокола, см. определение Swift для NSObjectProtocol, он имеет следующую функцию: func соответствуетToProtocol (aProtocol: Protocol!) -> Bool И вы можете точно определить свой собственный метод. Но я все еще пытаюсь понять, как передать что-то в этот метод! –  Sam 04.06.2014 в 21:23
  • Спасибо Сэм, я все еще пытаюсь понять, как я могу вызвать метод concsToProtocol! –  Jean Lebrument 04.06.2014 в 21:25
  • Чистая быстрая реализация в моем ответе здесь: stackoverflow.com/a/34995198/5389500 –  eonist 25.01.2016 в 15:47

5 ответов

11

В одном из ваших комментариев вы говорите:

«Я хочу создать метод, который возвращает массив типа класса, который реализует желаемый протокол.»

Вы пробовали что-то вроде следующего:

//notice the use of @objc here
@objc protocol AlertProtocol
{
    func getMyName()->String
}

class Class1 : AlertProtocol
{
     let name = "Object 1"
     func getMyName() -> String
    {
        return name
    }
}

class Class2 : AlertProtocol
{
    let name = "Object 2"
    func getMyName() -> String
    {
        return name
    }
}

//borrowing from and refactoring siLo's answer
func classesConformingToProtocol(proto:Protocol) -> [AnyClass]
{
    let availableClasses : [AnyClass] = [ Class1.self, Class2.self ]

    var conformingClasses = Array<AnyClass>()

    for myClass : AnyClass in availableClasses
    {
        if myClass.conforms(to: proto)
        {
            conformingClasses.append(myClass)
        }
    }

    return conformingClasses
}

Затем используйте приведенную выше структуру следующим образом:

let classes = classesConformingToProtocol(AlertProtocol.self)

Сложная часть, которая выполняет эту работу, - это "@objc", который выставляет протокол в целевую среду выполнения c и позволяет нам передавать любой "Тип протокола" в качестве параметра.

Вероятно, в какой-то момент в будущем мы сможем сделать это «чистым» способом Свифта.

    
ответ дан joakim 27.11.2014 в 04:14
  • В Swift 3, replacesToProtocol (Protocol) был заменен на соответствие (to: AnyObject.Type) –  Macabeus 13.03.2017 в 02:46
4

Вот что я пробовал:

@objc protocol Walker
{
    func walk()
}

@objc protocol Runner
{
    func run()
}

@objc class Zombie : Walker
{
    func walk () { println("Brains...") }
}

@objc class Survivor : Runner
{
    func run() { println("Aaaah, zombies!") }
}

func classesConformingToProtocol(proto:Protocol) -> AnyClass[]
{
    let availableClasses : AnyClass[] = [ Zombie.self, Survivor.self ]

    var conformingClasses = Array<AnyClass>()

    for myClass : AnyClass in availableClasses
    {
        if myClass.conformsToProtocol(proto)
        {
            conformingClasses.append(myClass)
        }
    }

    return conformingClasses
}

// This does not work
let walkers = classesConformingToProtocol(Walker.self)
let runners = classesConformingToProtocol(Runner.self)

Мне не удалось преобразовать информацию Swift Metatype в объект Protocol .

    
ответ дан Erik 05.06.2014 в 09:08
  • Я хочу создать метод, который возвращает массив типа класса, который реализует требуемый протокол. –  Jean Lebrument 05.06.2014 в 09:24
  • Есть ли у вас идея о том, как передать протокол в качестве параметра? –  Jean Lebrument 06.06.2014 в 15:26
  • Конкретный протокол или любой тип протокола? –  Erik 06.06.2014 в 16:59
  • Что вы имеете в виду с любым типом протокола? Идея заключается в том, что я бы написал метод, который принимает в качестве параметра протокол и возвращает массив со всеми классами в моем проекте, который реализует протокол, переданный в параметре. –  Jean Lebrument 06.06.2014 в 17:14
  • Ах, вам нужно будет использовать аргументы AnyClass, который является типом метаданных для класса и протоколов. Я пересмотрю свой ответ. –  Erik 06.06.2014 в 17:23
Показать остальные комментарии
1

В Swift 2.0 я уже использовал это так:

classA.conformsToProtocol(XXXProtocol.self as! Protocol)

Это не работает нормально ...

Посмотрите определение Protocol:

// All methods of class Protocol are unavailable. 
// Use the functions in objc/runtime.h instead.

@available(iOS 2.0, *)
public class Protocol {
}

Все они недоступны ... и я не знаю, что использовать вместо этого в objc / runtime.h

Так что я должен использовать этот метод:

if ClassA is protocol<XXXProtocol> {
    // do something
}

В настоящее время это работает ...

    
ответ дан Klein Mioke 01.12.2015 в 08:17
0

Если вы не разрешаете использовать @objc (потому что ваши протоколы имеют свойство, например), единственное решение, которое я нашел, - это закрытие. Затем вам нужно использовать замыкание, чтобы использовать протокол и вернуть значение.

protocol Proto { }
protocol Proto2 { }
class Foo: Proto { }
class Bar: Proto, Proto2 { }
class Baz: Proto2 { }
class Qux { }

func printConforms(classList: [AnyClass], protoCond: (AnyClass) -> Any?) {
    for i in classList {
        print(i, terminator: " -> ")
        if protoCond(i) != nil {
            print("is subscriber")
        } else {
            print("NOT IS subscriber")
        }
    }
}

let myClasses: [AnyClass] = [Foo.self, Bar.self, Baz.self, Qux.self]
printConforms(classList: myClasses, protoCond: { $0 as? Proto.Type })

Более полный пример: Ссылка

Edit

Еще одним лучшим решением является использование дженериков, например:

protocol Proto { }
class Foo: Proto { }
class Bar: Proto { }
class Baz { }

func filter<T>(classes: [AnyClass], byConformanceTo: T.Type) -> [AnyClass] {
    return classes.filter { $0 is T }
}

filter(classes: [Foo.self, Bar.self, Baz.self], byConformanceTo: Proto.Type.self)
// return [Foo.self, Bar.self]
    
ответ дан Macabeus 14.03.2017 в 07:13
-1

Разобраться сегодня (Xcode 6.1):

Во-первых, протокол должен быть помечен как @objc, чтобы любая проверка работала. Затем используйте приведение типа "if let" для проверки соответствия.

@objc protocol MyProtocol {
    var protocolValue: Int { get set }
}

if let conformingObject = someObject as? MyProtocol {
   // conformingObject is now someObject cast to MyProtocol
   conformingObject.protocolValue = 3
}
    
ответ дан SarahR 30.10.2014 в 02:59
  • Это не совсем цель моего вопроса. Мой вопрос заключался в том, как передать тип протокола методу. Не проверять соответствие протокола var протоколу. –  Jean Lebrument 30.10.2014 в 09:42
  • Извините за путаницу. Я действительно отвечал на ваш комментарий выше, где вы сказали: «Спасибо Сэму, я все еще пытаюсь понять, как я могу вызвать метод concsToProtocol!» –  SarahR 01.11.2014 в 02:56