admin管理员组文章数量:1123706
What is the rationale behind Swift compiler being overly strict about Protocol conformances? For instance, consider the following code:
protocol TestProtocol {
func testFunction()
}
actor TestClass: TestProtocol {
func testFunction() {
}
}
The build fails with the error saying Actor-isolated instance method 'testFunction()' cannot be used to satisfy nonisolated protocol requirement
.
But isn't the whole purpose of actor
thread safety and preventing data races? If so, where is the data race in the above code or conformance to protocol in general?
Edit: Based on the answers received from @Sweeper and @paulw11, here is the code I wrote and it crashes at runtime with error Incorrect actor executor assumption
import Foundation
protocol TestProtocol {
func increment()
func decrement()
}
actor TestActor: @preconcurrency TestProtocol {
var i = 5
func increment() {
i = i + 1
print("incremented \(i)")
}
func decrement() {
i = i - 1
print("decremented \(i)")
}
}
class TestClass {
let testActor = TestActor()
func g(x: any TestProtocol) {
x.increment() // this synchronously calls an isolated method!
x.decrement()
}
func f() {
print("inside f")
g(x: testActor)
}
}
What is the rationale behind Swift compiler being overly strict about Protocol conformances? For instance, consider the following code:
protocol TestProtocol {
func testFunction()
}
actor TestClass: TestProtocol {
func testFunction() {
}
}
The build fails with the error saying Actor-isolated instance method 'testFunction()' cannot be used to satisfy nonisolated protocol requirement
.
But isn't the whole purpose of actor
thread safety and preventing data races? If so, where is the data race in the above code or conformance to protocol in general?
Edit: Based on the answers received from @Sweeper and @paulw11, here is the code I wrote and it crashes at runtime with error Incorrect actor executor assumption
import Foundation
protocol TestProtocol {
func increment()
func decrement()
}
actor TestActor: @preconcurrency TestProtocol {
var i = 5
func increment() {
i = i + 1
print("incremented \(i)")
}
func decrement() {
i = i - 1
print("decremented \(i)")
}
}
class TestClass {
let testActor = TestActor()
func g(x: any TestProtocol) {
x.increment() // this synchronously calls an isolated method!
x.decrement()
}
func f() {
print("inside f")
g(x: testActor)
}
}
Share
Improve this question
edited 23 hours ago
Deepak Sharma
asked yesterday
Deepak SharmaDeepak Sharma
6,43910 gold badges66 silver badges156 bronze badges
2 Answers
Reset to default 1You can refer to protocols in your code with the concrete type being determined at runtime.
Consider:
protocol TestProtocol: {
func testFunction()
}
func doSomething(x:TestProtocol) {
x.testFunction()
}
The compiler is happy with this because it knows that any object that conforms to TestProtocol
will implement testFunction
and therefore everything will be fine at runtime.
But functions in an actor
need to be asynchronous, which means that they need to be await
ed.
If I was permitted to define
actor TestActor: TestProtocol
There would be a problem with doSomething
- It isn't expecting an asynchronous function.
To prevent this, the compiler will not allow an actor to implement a non-isolated protocol.
You need your protocol to inherit from Actor
:
Once you do that, the compiler will tell you that testFunction
needs to be async
protocol TestProtocol: Actor {
func testFunction() async
}
actor TestClass: TestProtocol {
func testFunction() async {
print("Test function")
}
}
And now, the compiler knows that invocations of testFunction
must be asynchronous:
func doSomething(x:TestProtocol) {
Task {
await x.testFunction()
}
}
where is the data race in the above code or conformance to protocol in general?
Normally, if you want to call an actor's isolated method from a context not isolated to that actor instance, the call must be asynchronous (you need to write await
). You are submitting a job to the serial executor, which the serial executor will execute when it's become free. This is why this is an asynchronous call, and this is how an actor synchronises access to its state.
Your code, if it were allowed, allows you to call an actor's method synchronously!
let instance = TestClass()
func f() {
g(x: instance)
}
func g(x: any TestProtocol) {
x.testFunction() // this synchronously calls an isolated method!
}
Now imagine many different threads concurrently calling g(x: instance)
, and that testFunction
mutates some state that should have been protected by the actor. The actor cannot serialise these calls because they are not asynchronously waiting for the serial executor to become free, and you end up with data races.
You can change the protocol to inherit from Actor
:
protocol TestProtocol: Actor {
func testFunction()
}
Now the compiler understands that testFunction
must be a function isolated to some actor, so the isolated testFunction
in TestClass
can satisfy the protocol requirement. Calling it synchronously in g
will now produce an error.
本文标签: iosActors conformance to ProtocolsStack Overflow
版权声明:本文标题:ios - Actors conformance to Protocols - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736586061a1945012.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论