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

时间:2014-08-13 15:32:19

标签: ios swift protocols

在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;
}

我在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;
}

试图在Swift中复制这种行为,我发现我们无法像在obj-C中那样访问语言等效的“运行时”API,并且可以理解,因为swift是一项正在进行的工作并且给人们这种访问无疑是有风险的。

但这也意味着我们不能以相同的方式使用协议,即在任何协议的意义上。

我想到的第一个可能的解决方法是混合泛型任何其中,但这对于某些事情来说太过分了过去很简单。

所以,我的问题是:在Swift中传递协议(如 ANY协议)的一些提议解决方案是什么?

编辑:我使用Swift中引入的元类型类型取得了一些成功,这在语言设计方面是有意义的,但也没有提供提供“字符串”表示形式的能力。元数据类型可以用作字典中的键。

这当然是随着语言的成熟而添加的功能。

2 个答案:

答案 0 :(得分:1)

你有什么尝试?

这样的事情不起作用:

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
  }

  //...
}

答案 1 :(得分:0)

我尝试了很长时间,有许多不同的方法(指针,Protocol),而没有@objc的唯一解决方案和我发现的纯Swift是关闭的。然后,您需要使用闭包来使用协议并返回值

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

更完整的示例:https://gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9

修改

另一个更好的解决方案是使用泛型,例如:

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]