In Swift, can you find all types in a module that adhere to a specific protocol?

做~自己de王妃 提交于 2019-12-22 10:08:21

问题


In Swift 4, is it possible to find all types within the current module which adhere to a specific protocol?

For instance, say I've defined this protocol and these classes:

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

class Dog : Animal{
}

class Cat : Animal, Favorite{
}

class Car : Vehicle{
}

class Bicycle : Vehicle, Favorite{
}

I want to find all types that implement Favorite. This can be easily done in C#, but I’m not sure if you can in Swift.

  • Cat
  • Bicycle

If it helps, I'm using Swift 4.


回答1:


I don't believe Swift currently has a 'native' (not dependant on the Objective-C runtime) API for doing this kind of reflection.

However, if you're on an Apple platform (and therefore have Obj-C interoperability), you could get a list of all classes registered with the Obj-C runtime, and then filter those that conform to a given protocol. This will work for Swift classes (even those that don't inherit from NSObject) because under the hood Swift classes are built on top of Obj-C classes (when there's Obj-C interop).

Because this will only work for classes (not structures or enumerations), you may want to restrict your protocol such that only classes can conform to it:

protocol Favorite : class {}

You can then do the following, using 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]

Here we're calling compactMap(_:) on the UnsafeBufferPointer to get back an array of metatypes that represent types conforming to Favorite (i.e those that can be cast to the existential metatype Favorite.Type).




回答2:


No it is not possible to get names of classes inside a module.




回答3:


You can count by your code, use extension protocol to count like below code :)

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()
  }
}


来源:https://stackoverflow.com/questions/47788562/in-swift-can-you-find-all-types-in-a-module-that-adhere-to-a-specific-protocol

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!