简体   繁体   English

在Swift中将协议类型作为参数传递

[英]Passing Protocol Type as parameter in Swift

In objective-C we can (by importing the language's runtime header file) do the following: 在objective-C中,我们可以(通过导入语言的运行时头文件)执行以下操作:

//Pass a service (usually an object) and ANY protocol
- (void)registerService:(id)service forProtocol:(Protocol *)protocol
{
    //Grab the protocol's name (that's why we import runtime.h, it contains the protocol_getname mehod)
    NSString *protocolName = [NSString stringWithUTF8String:protocol_getName(protocol)];

    //If the object we passed does not conform to the protocol, inform and break
    if (![service conformsToProtocol:protocol])
    {
        NSLog(@"Service: %@ does not conform to protocol: %@", service, protocolName);
        return;
    }

    //Else add service in a collection (array, dictionary) for later use
    self.services[protocolName] = service;
}

I use this in obj-C as a "poor man's IOC container", a simple registry used for injecting dependencies. 我在obj-C中使用它作为“穷人的IOC容器”,一个用于注入依赖项的简单注册表。

//The interested party uses this method to obtain the dependency it needs by asking for the object that is registered as responsible for conforming to the Protocol parameter
- (id)serviceForProtocol:(Protocol *)protocol
{
    id result;

    NSString *protocolName = [NSString stringWithUTF8String:protocol_getName(protocol)];

    //Look for the service that conforms to the protocol in the registry dictionary,
    result = self.services[protocolName];

    //if there is no object meeting the criteria, inform/alert
    if (result == nil)
    {
        NSLog(@"No class registered for protocol: %@", protocolName);
    }

    //and return the result
    return result;
}

Trying to replicate this behaviour in Swift, I found out that we don't have access to the language's equivalent "runtime" API like we do in obj-C (yet), and understandably so since swift is a work in progress and giving people this kind of access is undoubtedly risky. 试图在Swift中复制这种行为,我发现我们无法像在obj-C中那样访问该语言的等效“运行时”API,并且可以理解,因为swift是一项正在进行的工作并且给予了人们这种访问无疑具有风险。

But this also means that we can't use Protocol in the same way anymore ie in the sense of ANY protocol. 但这也意味着我们不能再以相同的方式使用协议,即在任何协议的意义上。

The first possible workaround that comes to mind is a mix of generics , Any and where , but that feels too much for something that used to be simple. 我想到的第一个可能的解决方法是混合使用泛型Anywhere ,但对于过去简单的东西来说,感觉太多了。

So, my question is: What are some proposed solutions for passing around Protocol (as in ANY Protocol ) in Swift? 所以,我的问题是:在Swift中传递协议 (如在任何协议中 )的一些提议解决方案是什么?

EDIT: I had some success using the Metatype Type introduced in Swift which makes sense in terms of language design but also does not (yet) provide the ability to provide a "String" representation of a Metatype that could be used as a key in a dictionary. 编辑:我使用Swift中引入的元类型类型取得了一些成功,这在语言设计方面是有意义的,但也没有提供能够提供元数据类型的“字符串”表示的能力,该元数据类型可用作字典。

This is of course a feature that could be added as the language matures. 这当然是随着语言的成熟而添加的功能。

What have you tried? 你有什么尝试?

Does something like this not work: 这样的事情不起作用:

import Foundation

func registerService(service: NSObjectProtocol, forProtocol prot: Protocol) {
  let protocolName = NSStringFromProtocol(prot)

  if (!service.conformsToProtocol(prot)) {
    println("Service: \(service) does not conform to protocol: \(protocolName)")
    return
  }

  //...
}

I tried for a long time, with many different ways (pointers, Protocol ), and the only solution without @objc and pure Swift that I found is with closure. 我尝试了很长时间,有许多不同的方法(指针, Protocol ),并且我发现没有@objc和纯Swift的唯一解决方案是关闭。 Then, you need use a closure to use a protocol and return a value 然后,您需要使用闭包来使用协议并返回一个值

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

More complete example: https://gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9 更完整的例子: https//gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9

Edit 编辑

Another better solution is using generics, for example: 另一个更好的解决方案是使用泛型,例如:

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]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM