首页 文章

协议的Swift扩展存储

提问于
浏览
9

我制定了一个协议:

protocol SomeProtocol {
    func getData() -> String
}

我创建一个符合它的结构:

struct SomeStruct: SomeProtocol {
    func getData() -> String {
        return "Hello"
    }
}

现在我希望每个 UIViewController 都有一个名为 source 的属性,所以我可以做类似......

class MyViewController : UIViewController {
    override func viewDidLoad() {
        self.title = source.getData()
    }
}

为此,我创建了一个协议来定义属性:

protocol SomeProtocolInjectable {
    var source: SomeProtocol! { get set }
}

现在我只需要使用以下属性扩展视图控制器:

extension UIViewController: SomeProtocolInjectable {
    // ???
}

如何将存储的属性与协议类型一起使用?

什么没用:

还有其他建议吗?

3 回答

  • 0

    任何协议对象都可以转换为类型擦除类 . 构建一个 AnySomeProtocol 并存储它 .

    private var sourceKey: UInt8 = 0
    
    final class AnySomeProtocol: SomeProtocol {
        func getData() -> String { return _getData() }
        init(_ someProtocol: SomeProtocol) { _getData = someProtocol.getData }
        private let _getData: () -> String
    }
    
    extension UIViewController: SomeProtocolInjectable {
        var source: SomeProtocol! {
            get {
                return objc_getAssociatedObject(self, &sourceKey) as? SomeProtocol
            }
            set(newValue) {
                objc_setAssociatedObject(self, &sourceKey, AnySomeProtocol(newValue), .OBJC_ASSOCIATION_RETAIN)
            }
        }
    }
    
    class MyViewController : UIViewController {
        override func viewDidLoad() {
            self.title = source.getData()
        }
    }
    

    调用者只能使用它来访问协议方法 . 您无法使用 as 将其强制恢复为原始类型,但无论如何都应该避免这种情况 .

    作为旁注,我真的建议让 source 返回 SomeProtocol? 而不是 SomeProtocol! . 这里没有任何承诺 source 将被设定 . 你甚至没有设置它直到 viewDidLoad .

  • 3

    你可以使用静态和视图控制器 hash

    struct SomeProtocol {/*....*/}
    
    struct DataProxy {
        var data: [Int: SomeProtocol]
    }
    
    
    
    protocol SomeProtocolInjectable {
        var source: SomeProtocol! { get set }
    }
    
    extension UIViewController: SomeProtocolInjectable {
    
        static var dataProxy = DataProxy(data: [:])
    
        var source: SomeProtocol! {
            get{
                return UIViewController.dataProxy.data[self.hashValue]
            }
            set{
                UIViewController.dataProxy.data[self.hashValue] = newValue
            }
        }
    
    }
    
  • 3

    如何为 getData() 添加一个默认实现,为协议提供一个虚拟结构实现,并将其用作 source 变量的默认值:

    protocol SomeProtocol {
        func getData() -> String
    }
    
    extension SomeProtocol {
        func getData() -> String {
            return "Hello"
        }
    }
    
    protocol SomeProtocolInjectable {
        var source: SomeProtocol { get set }
    }
    
    struct DummyProtocolImplementation: SomeProtocol {
    
    }
    
    class MyViewController : UIViewController {
        var _source: SomeProtocol = DummyProtocolImplementation()
    
        override func viewDidLoad() {
            self.title = source.getData()
        }
    }
    
    extension MyViewController: SomeProtocolInjectable {
        var source: SomeProtocol { get { return _source } set { _source = newValue } }
    }
    

    我冒昧地扩展 MyViewController 而不是 UIViewController ,因为后者无论如何都不知道协议 .

相关问题