英文:
Options to init with @MainActor property as default value
问题
I have translated the provided text:
我有一个类,直到现在在其初始化期间使用 UIApplication.shared
作为默认参数。它的简化版本如下:
final class MyClass {
private var application: UIApplication
init(application: UIApplication = UIApplication.shared) {
self.application = application
}
}
使用 Strict Concurrency Checking
选项时,现在会出现以下错误:
> 无法从非隔离上下文引用主要角色隔离的类属性 'shared'
这是因为 UIApplication
被标记为 @MainActor
,因此其所有属性都是异步的:
@available(iOS 2.0, *)
@MainActor open class UIApplication : UIResponder {
open class var shared: UIApplication { get }
...
}
我看到两个解决方案:
#解决方案1
将属性标记为可选项,并在 init
期间初始化它。在某些情况下,这可能看起来是个好主意,但这样我们就失去了依赖注入属性并使其可测试的能力。此外,在我们知道属性确实已初始化的情况下,使用可选项会感到不便:
final class MyClass {
private var application: UIApplication?
@MainActor
init() {
self.application = UIApplication.shared
}
}
#解决方案2
让调用者负责在 @MainActor
上初始化类:
final class MyClass {
private var application: UIApplication
init(application: UIApplication) {
self.application = application
}
// 仅显示调用者将执行的内容
@MainActor
func option1ToInit() {
_ = MyClass(application: UIApplication.shared)
}
// 仅显示调用者将执行的内容
func option2ToInit() {
Task {
_ = await MyClass(application: UIApplication.shared)
}
}
}
第二种解决方案似乎效果不错,但我需要做一些重构工作,以便所有调用者都在 @MainActor
上执行。我想知道是否还有其他我忽略的选项。
英文:
I have a class which up until now used the UIApplication.shared
as a default argument during it's initialisation. The simplified version of it:
final class MyClass {
private var application: UIApplication
init(application: UIApplication = UIApplication.shared) {
self.application = application
}
}
With Strict Concurrency Checking
using the Complete
option now gives the following error:
> Main actor-isolated class property 'shared' can not be referenced
> from a non-isolated context
It makes sense since UIApplication
is marked as @MainActor
hence, all it's properties are async:
@available(iOS 2.0, *)
@MainActor open class UIApplication : UIResponder {
open class var shared: UIApplication { get }
...
}
I see two solutions for this:
#Solution1
Mark the property as optional and initialise it during the init
. In some cases it may seem as a good idea, but with this we loose the ability to dependency inject the property and make it testable. Also using optionals further when we know the property is indeed initialised is uncomfortable:
final class MyClass {
private var application: UIApplication?
@MainActor
init() {
self.application = UIApplication.shared
}
}
#Solution2
Let's make the caller responsible for initialising the class on the @Mainactor
:
final class MyClass {
private var application: UIApplication
init(application: UIApplication) {
self.application = application
}
// Just to present what the caller would do
@MainActor
func option1ToInit() {
_ = MyClass(application: UIApplication.shared)
}
// Just to present what the caller would do
func option2ToInit() {
Task {
_ = await MyClass(application: UIApplication.shared)
}
}
}
The second solution seems to work nicely, however I need to do a bit of refactor work so all the callers are executing on the @MainActor
. I was wondering if there is any other option I am missing here.
答案1
得分: 1
Sure, here's the translation of the code part:
如果您可以将@MainActor
添加到init(根据您的描述似乎可以这样做),那么您只需要将.shared
的访问移动到init中:
@MainActor
init(application: UIApplication? = nil) {
self.application = application ?? .shared
}
英文:
If you can add @MainActor
to init (which it seems you can from your description), then you just need to move the accessing of .shared
into the init:
@MainActor
init(application: UIApplication? = nil) {
self.application = application ?? .shared
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论