Можете ли вы в Swift найти все типы в модуле, которые придерживаются определенного протокола?

В Swift 4 можно ли найти все типы в текущем модуле, которые придерживаются определенного протокола?

Например, скажем, я определил этот протокол и эти классы:

protocol Animal{}
protocol Vehicle{}
protocol Favorite{}

class Dog : Animal{
}

class Cat : Animal, Favorite{
}

class Car : Vehicle{
}

class Bicycle : Vehicle, Favorite{
}

Я хочу найти все типы, реализующие Favorite. Это можно легко сделать в C#, но я не уверен, что вы сможете это сделать в Swift.

  • Кошка
  • Велосипед

Если это поможет, я использую Swift 4.


person Mark A. Donohoe    schedule 13.12.2017    source источник
comment
Это будет работать только для классов @objc   -  person Leonid Usov    schedule 15.04.2018


Ответы (3)


Я не верю, что Swift в настоящее время имеет «родной» (не зависящий от среды выполнения Objective-C) API для такого отражения.

Однако, если вы работаете на платформе Apple (и, следовательно, имеете совместимость с Obj-C), вы можете получить список всех классов, зарегистрированных в среде выполнения Obj-C, а затем отфильтровать те, которые соответствуют заданный протокол. Это будет работать для классов Swift (даже тех, которые не наследуются от NSObject), потому что под капотом классы Swift строятся поверх классов Obj-C (когда есть взаимодействие Obj-C).

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

protocol Favorite : class {}

Затем вы можете сделать следующее, используя objc_copyClassList:

import Foundation

protocol Animal {}
protocol Vehicle {}
protocol Favorite : class {}

class Dog : Animal {}
class Cat : Animal, Favorite {}
class Car : Vehicle {}
class Bicycle : Vehicle, Favorite {}

/// Invokes a given closure with a buffer containing all metaclasses known to the Obj-C
/// runtime. The buffer is only valid for the duration of the closure call.
func withAllClasses<R>(
  _ body: (UnsafeBufferPointer<AnyClass>) throws -> R
) rethrows -> R {

  var count: UInt32 = 0
  let classListPtr = objc_copyClassList(&count)
  defer {
    free(UnsafeMutableRawPointer(classListPtr))
  }
  let classListBuffer = UnsafeBufferPointer(
    start: classListPtr, count: Int(count)
  )

  return try body(classListBuffer)
}
//                               .flatMap in Swift < 4.1
let classes = withAllClasses { $0.compactMap { $0 as? Favorite.Type } }
print(classes) // [Bicycle, Cat]

Здесь мы вызываем compactMap(_:) для UnsafeBufferPointer, чтобы вернуть массив метатипов, представляющих типы, соответствующие Favorite (то есть те, которые можно привести к экзистенциальному метатипу Favorite.Type).

person Hamish    schedule 13.12.2017

Нет, невозможно получить имена классов внутри модуля.

person Rahul Dasgupta    schedule 13.12.2017

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

protocol Animal{}
protocol Vehicle{}
protocol Favorite{
  func countImplement()
}

var totalImplement = 0
extension Favorite {
  func updateTotalImplement(){
    totalImplement += 1
  }
}

class Dog : Animal{
}

class Cat : Animal, Favorite{
   func countImplement(){
    this.updateTotalImplement()
  }
}

class Car : Vehicle{
}

class Bicycle : Vehicle, Favorite{
   func countImplement(){
    this.updateTotalImplement()
  }
}
person Sua Le    schedule 13.12.2017
comment
Проблема в том, что вы должны знать, что это за типы. Кроме того, вы должны добавлять что-то к этим типам, что невозможно в моем случае, поскольку я пытаюсь обнаружить существующие классы, а не создавать их. Короче говоря, я пытаюсь написать функцию, которая обнаруживает все типы в модуле, которые придерживаются протокола. Вы просто определяете тип, реализуете протокол, и он автоматически обнаруживается. - person Mark A. Donohoe; 13.12.2017
comment
Иди с ответом Хэмиша :) - person Sua Le; 14.12.2017