在Swift中,我怎么能声明一个符合一个或多个协议的特定types的variables?
在Swift中,我可以通过声明如下来明确地设置一个variables的types:
var object: TYPE_NAME
如果我们想更进一步,并声明一个符合多个协议的variables,我们可以使用protocol
声明:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
如果我想声明一个符合一个或多个协议并且也是特定基类types的对象呢? Objective-C的等价物看起来像这样:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
在Swift中,我期望它看起来像这样:
var object: TYPE_NAME,ProtocolOne//etc
这使我们能够灵活处理基本types的实现以及协议中定义的添加接口。
还有另一个更明显的方式,我可能会失踪?
例
作为一个例子,说我有一个UITableViewCell
工厂负责返回符合协议的单元格。 我们可以很容易地设置一个返回符合协议的单元的通用函数:
class CellFactory { class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T { //etc } }
后来我想把这些细胞出院,同时利用types和协议
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
这将返回一个错误,因为表视图单元格不符合协议…
我想能够指定单元格是一个UITableViewCell
并符合variables声明中的MyProtocol
?
理由
如果您熟悉Factory Pattern,那么在能够返回实现特定接口的特定类的对象的情况下,这是有意义的。
就像在我的例子中,有时候我们喜欢定义在应用到特定对象时有意义的接口。 我的表格视图单元格的例子就是这样一个理由。
虽然提供的types并不完全符合所提到的接口,工厂返回的对象所以我希望灵活性与基类types和声明的协议接口
在Swift 4中,现在可以声明一个types的子类的variables,并同时实现一个或多个协议。
var myVariable: MyClass & MyProtocol & MySecondProtocol
或作为方法的参数:
func shakeEm(controls: [UIControl & Shakeable]) {}
苹果公司在WWDC2017 会议上宣布了这一点:在Swift中有什么新鲜事
其次,我想谈谈组成类和协议。 所以,在这里我介绍了一个UI元素的可摇动协议,可以给予一点震动的效果来吸引注意力本身。 我已经提前并扩展了一些UIKit类来实际提供这种摇动function。 现在我想写一些看似简单的东西。 我只想写一个函数,它需要一些可摇动的控件,并且摇动那些能够引起注意力的控件。 我可以在这里写什么types的数组? 这实际上令人沮丧和棘手。 所以,我可以尝试使用UI控件。 但并不是所有的UI控件都可以在这个游戏中使用。 我可以尝试动摇,但不是所有的动摇是UI控制。 实际上,在Swift 3中没有什么好的方法来表示。Swift 4介绍了用任意数量的协议组成一个类的概念。
你不能像这样声明variables
var object:Base,protocol<ProtocolOne,ProtocolTwo> = ...
也不像声明函数的返回types
func someFunc() -> Base,protocol<MyProtocol,Protocol2> { ... }
你可以像这样声明一个函数参数,但是它基本上是上传的。
func someFunc<T:Base where T:protocol<MyProtocol1,MyProtocol2>>(val:T) { // here, `val` is guaranteed to be `Base` and conforms `MyProtocol` and `MyProtocol2` } class SubClass:BaseClass, MyProtocol1, MyProtocol2 { //... } let val = SubClass() someFunc(val)
到目前为止,你所能做的就是:
class CellFactory { class func createCellForItem(item: SpecialItem) -> UITableViewCell { return ... // any UITableViewCell subclass } } let cell = CellFactory.createCellForItem(special) if let asProtocol = cell as? protocol<MyProtocol1,MyProtocol2> { asProtocol.protocolMethod() cell.cellMethod() }
有了这个,技术上的cell
就和asProtocol
。
但是,对于编译器, cell
只有UITableViewCell
接口,而asProtocol
只有协议接口。 所以,当你想调用UITableViewCell
的方法,你必须使用cell
variables。 当你想调用协议方法时,使用asProtocol
variables。
如果你确定单元格符合协议,你不必使用, if let ... as? ... {}
if let ... as? ... {}
。 喜欢:
let cell = CellFactory.createCellForItem(special) let asProtocol = cell as protocol<MyProtocol1,MyProtocol2>
不幸的是,Swift不支持对象级的协议一致性。 然而,这可能会有一些尴尬的解决方法,可以达到您的目的。
struct VCWithSomeProtocol { let protocol: SomeProtocol let viewController: UIViewController init<T: UIViewController>(vc: T) where T: SomeProtocol { self.protocol = vc self.viewController = vc } }
然后,在任何需要做UIViewController的地方,你都可以访问struct的.viewController方面以及任何需要协议方面的东西,你可以参考.protocol。
例如:
class SomeClass { let mySpecialViewController: VCWithSomeProtocol init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol { self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController) } }
现在,无论何时您需要使用mySpecialViewController来执行与UIViewController相关的任何操作,您只需引用mySpecialViewController.viewController,只要您需要执行一些协议function,就可以引用mySpecialViewController.protocol。
希望Swift 4将允许我们在将来使用附加的协议来声明一个对象。 但现在,这是有效的。
希望这可以帮助!
我曾尝试将故事板中的通用交互器连接(IB不会允许您将sockets连接到协议,只有对象实例),我曾经有过类似的情况,我只是用私有计算机屏蔽基类public ivar属性。 虽然这并不妨碍某人进行非法分配,但确实提供了一种方便的方法来安全地防止在运行时与不合规的实例进行不必要的交互。 (即阻止调用委托方法到不符合协议的对象。)
例:
@objc protocol SomeInteractorInputProtocol { func getSomeString() } @objc protocol SomeInteractorOutputProtocol { optional func receiveSomeString(value:String) } @objc class SomeInteractor: NSObject, SomeInteractorInputProtocol { @IBOutlet var outputReceiver : AnyObject? = nil private var protocolOutputReceiver : SomeInteractorOutputProtocol? { get { return self.outputReceiver as? SomeInteractorOutputProtocol } } func getSomeString() { let aString = "This is some string." self.protocolOutputReceiver?.receiveSomeString?(aString) } }
“outputReceiver”声明为可选项,私有“protocolOutputReceiver”也是如此。 通过总是通过后者(计算属性)访问outputReceiver(aka委托),我可以有效地过滤掉任何不符合协议的对象。 现在我可以简单地使用可选的链接来安全地调用委托对象,不pipe它是否实现协议,甚至存在。
要将这个应用于你的情况,你可以让公共ivar的types为“YourBaseClass?” (而不是AnyObject),并使用私有的计算属性来执行协议一致性。 FWIW。
编辑: 我错了 ,但如果有人读这个像我这样的误解,我离开这个答案。 OP询问检查给定子类的对象的协议一致性,这是接受的答案显示的另一个故事。 这个答案是关于基类的协议一致性的。
也许我错了,但你不是在谈论添加协议一致性的UITableCellView
类? 在这种情况下协议扩展到基类,而不是对象。 请参阅苹果公司关于使用扩展名声明协议采用的文档,在你的情况下是这样的:
extension UITableCellView : ProtocolOne {} // Or alternatively if you need to add a method, protocolMethod() extension UITableCellView : ProcotolTwo { func protocolTwoMethod() -> String { return "Compliant method" } }
除了已经引用的Swift文档之外,还可以参考Nate Cooks的文章“不兼容types的generics函数”以及更多示例。
这使我们能够灵活处理基本types的实现以及协议中定义的添加接口。
还有另一种更明显的方式,我可能会失踪?
协议采用将做到这一点,使一个对象遵守给定的协议。 但是,要知道不利的一面,一个给定的协议types的variables不知道协议以外的任何东西。 但是,这可以通过定义一个协议来规避所有需要的方法/variables/ …
虽然提供的types并不完全符合所提到的接口,工厂返回的对象所以我希望灵活性与基类types和声明的协议接口
如果你想要一个通用的方法,variables符合协议和基类的types,你可能会运气不好。 但是这听起来像是你需要定义足够宽的协议以获得所需的一致性方法,同时又足够窄以便能够在不需要太多工作的情况下将它用于基类(即只是声明一个类符合协议)。