【问题标题】:Not able to pass self (which implements a protocol) to init method of a class instantiation.(Swift)无法将 self (实现协议)传递给类实例化的 init 方法。(Swift)
【发布时间】:2016-05-03 05:51:27
【问题描述】:

我有这样的协议

public protocol FooProtocol {
    associatedType someType

    func doFoo(fooObject:someType) -> Void
}

我有一个实现这个协议的类

public class Foo<T> : FooProtocol {
    private let _doFoo : (T) -> Void

    init<P : FooProtocol where P.someType == T>(_dep : P) {
        _doFoo = _dep.doFoo
    }

    public func doFoo(fooObject: T) {
        _doFoo(fooObject)
    }
}

这里的一切对我来说都很好,现在在另一个类中我使用someType = MyType 实现 FooProtocol,然后当我尝试使用 T = MyType 初始化 Foo 类时,通过在 init 的 init 方法中传递 self Foo class 我得到一个编译错误,我在这里做错了什么?

错误信息:

" 无法使用 (_dep: NSObject -&gt; () -&gt; MyView) 类型的参数列表调用 init"

public class MyView : UIViewController, FooProtocol {

    func doFoo(fooObject : MyType) {
        ...
    }

    // Here I want to initialize the Foo<T> class
    let fooInstant : Foo<MyType> = Foo.init(_dep : self)
    // I get the error message on the above line the error
    // message reads "Cannot invoke init with an argument list
    // of type `(_dep: NSObject -> () -> MyView)`
}

someType == MyType 不符合 FooProtocol,因为我试图用T == MyType 初始化一个Foo 对象,这在技术上应该可行吗?

【问题讨论】:

  • 你能具体说明你想用这种方法实现什么目标吗?为什么?
  • 好的,我有一个具有执行异步任务的方法的类,我需要编写一个通用回调接口(协议),然后我可以在使用这些异步函数的类中实现它并将通用协议传递给这些函数作为参数,然后我可以在异步方法成功完成时进行回调
  • 好的,我猜你不需要任何初始化方法。您可以在viewController 中将委托添加到协议中,并且能够通过该委托回调协议方法。喜欢:let delegate: FooProtocol? 并且使用委托只需调用协议方法,例如:delegate?.dooFoo()

标签: ios swift swift2 swift-protocols


【解决方案1】:

这实际上似乎与您的泛型或协议一致性无关。只是您在初始化self 之前尝试在属性分配中访问self(默认属性值在初始化期间分配)。

因此,解决方案是使用像这样的惰性属性:

lazy var fooInstant : Foo<MyType> = Foo(_dep : self)

现在将在第一次访问时创建它,因此在初始化 self 之后。

【讨论】:

  • 是的,这行得通!我现在明白了,为了获得你需要从实例范围内访问它的自我,我一直试图从静态范围内访问自我,我讨厌这种愚蠢的语言
  • 即使进行了编辑,self 在加载类时也会在此时初始化。它最初不起作用的原因是类型不匹配,与初始化无关。理解这一点非常重要。
  • @avismara 我不确定你的意思...self 在其初始化程序之一被调用之前不会被初始化 - 并且您无法访问 self 上的任何实例成员,直到它有发生了,所以你不能把它当作一个实例。
  • 如果您将self 称为MyClass 的实例,则为true。同意。但是当你在方法/函数/闭包之外使用self 时,它的含义就完全不同了。它指的是MyClass 本身。这将在MyClass 加载到内存时被初始化。
  • @avismara 好的,谢谢你的澄清!我已经编辑了我的答案。
【解决方案2】:

在使用self 的地方了解它的上下文非常重要。在这种情况下,self 并不意味着 MyView 的实例。

但是,您可以通过以下方式使其工作:

let fooInstant : Foo<String> = Foo.init(_dep : MyView())

如果不了解您在这里要做什么,我不能说更多。

【讨论】:

  • 是的,你提到的有效,但那是一个不同的实例,而不是我想要的实例
  • 尽管@originaluser2 给出的原因对于此实现失败的原因是错误的,但请使用他们的解决方案。我想这就是你想要的。
  • @avismara 错了,怎么办?
  • @avismara 是的,我现在正在尝试他的解决方案
  • self 在上下文中实际上是另外一回事。这里self 实际上是Class MyView 本身,而不是它的一个实例。 self 在您在方法/闭包中使用它的情况下将是一个隐式参数,在这种情况下它会是真的。
【解决方案3】:

看来这行得通

public class MyView : UIViewController, FooProtocol {

    var fooInstant : Foo<MyType>!

    public func initializeFooInstant(){
        fooInstant  = Foo.init(_dep : self)
    }


    func doFoo(fooObject : MyType) {
    ...
    }
}

我认为系统不允许您访问 self 来初始化 let 属性,因为无法保证在初始化 let 属性时 self 已完全初始化。

在上述情况下,您只需确保在访问 fooInstance 之前对其进行初始化,否则程序将崩溃。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 2022-10-16
    • 2014-12-03
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    相关资源
    最近更新 更多