Open swift-ci opened 6 years ago
This is "fixed" in Swift 4.1 by noting that a class-bound protocol doesn't really count as AnyObject, even though it's convertible to it.
<stdin>:20:7: error: type 'Object' does not conform to protocol 'HasFactory'
class Object: NSObject, HasFactory {
^
<stdin>:4:20: note: unable to infer associated type 'T' for protocol 'HasFactory'
associatedtype T: AnyObject
^
<stdin>:21:14: note: inferred type 'IsDelegate' (by matching requirement 'delegate') is invalid: does not conform to 'AnyObject'
weak var delegate: IsDelegate?
^
@slavapestov, do you have the dup for this?
Comment by Nikola Lajic (JIRA)
Thanks @belkadan, at least with a warning I won't have to remember to sprinkle @objc. Is there a way to make this work in environments where Objective C is not available?
:-( Not really. There's a bit more discussion of the issue in SR-6039. You can store the delegate as AnyObject?
instead of IsDelegate?
, but that requires downcasting when you want to actually use it, which isn't a free operation. There are also some ridiculous tricks you can play with closures and weak captures and protocol extension methods, but I don't know if that's good for maintainability.
protocol HasFactory {
associatedtype T//: AnyObject
/*weak*/ var delegate: T? { get set }
}
extension IsDelegate {
func weaklyCaptureInClosureForSR7259() -> () -> IsDelegate? {
return { [weak self] in self }
}
}
class Object2: NSObject, HasFactory {
private var _delegateStorage: (() -> IsDelegate?)?
var delegate: IsDelegate? {
get { return _delegateStorage?() }
set { _delegateStorage = newValue?.weaklyCaptureInClosureForSR7259() }
}
}
@slavapestov, anything to add?
Comment by Nikola Lajic (JIRA)
Thanks for the workarounds. @objc will work in my case, but hopefully this gets fixed in a future version so it's not environment dependent.
Comment by Nikola Lajic (JIRA)
@belkadan searching for an alternative I tried to use generic classes and inheritance.
class A {
init() {}
}
class B<T: AnyObject>: A {
weak var delegate: T?
}
protocol Delegate {}
class C: B<Delegate> {}
Which throws an error:
static member 'init()' cannot be used on instance of type 'B<Delegate>'
Adding @objc to Delegate fixes the problem again.
Is this related or should I create a separate issue?
That's a bad error, but it's just as much not going to work as what you have. Non-@objc protocol values are not compatible with AnyObject-constrained generics. (That's the original SR-6039.)
You could file a separate bug for the error message, though.
Attachment: Download
Environment
macOS High Sierra 10.13.1 (17B1002) Xcode Version 9.2 (9C40b) Reproduced on multiple computersAdditional Detail from JIRA
| | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: 19add1cc56657194f435984ca891b63eIssue Description:
Calling a method, defined in a protocol, on a property defined in another protocol with an associated type causes an error:
The error will be thrown at the last line of the following code:
Workarounds:
Add @objc to IsDelegate protocol. Which works but is problematic because you have to declare all future delegate protocols as @objc, and there is no warning if you don't, so you risk a runtime crash.
In Object, declare delegate as a class instead of a protocol (in this example Delegate instead of IsDelegate). This avoids the problem but defeats the purpose, since Object is tied to a concrete implementation.