Ios 单例初始化后立即运行Swift代码
我有一些Swift代码,我想在我的singleton初始化后立即运行。此代码需要在我的单例初始化后运行的原因是,代码依赖于现有的单例 下面是我的目标代码示例。但这段代码似乎不起作用,因为Ios 单例初始化后立即运行Swift代码,ios,swift,Ios,Swift,我有一些Swift代码,我想在我的singleton初始化后立即运行。此代码需要在我的单例初始化后运行的原因是,代码依赖于现有的单例 下面是我的目标代码示例。但这段代码似乎不起作用,因为refreshData试图在单例完全创建之前访问它 class MyObject: NSObject { static let shared: MyObject = MyObject() let otherManager: OtherStuff = OtherStuff() var n
refreshData
试图在单例完全创建之前访问它
class MyObject: NSObject {
static let shared: MyObject = MyObject()
let otherManager: OtherStuff = OtherStuff()
var number: Int = 1
override init() {
super.init()
setup()
}
func setup() {
print("Setup code goes here")
otherManager.refreshData()
}
}
class OtherStuff {
func refreshData() {
MyObject.shared.number += 1
print(MyObject.shared.number)
}
}
在它上面打印设置代码在这里
,然后永远挂起。我正在使用的示例要复杂得多,但我相信这个小示例准确地再现了我遇到的问题
如何修复此问题,以便在运行代码之前等待设置单例和所有操作?下面是一个示例,它不调用无限共享对象,而是使用现有单例来完成其余工作
class MyObject: NSObject {
static let shared: MyObject = MyObject()
let otherManager: OtherStuff = OtherStuff()
var number: Int = 1
override init() {
super.init()
setup()
}
func setup() {
print("Setup code goes here")
otherManager.refreshData(self)
}
}
class OtherStuff {
func refreshData(_ myObject: MyObject) {
myObject.number += 1
print(myObject.number)
}
}
问题是,在
init
有机会返回之前,您正在调用setup
,从而将refreshData
消息发送到otherManager
,因此还没有MyObject。这可以通过从runloop跳出到下一个runloop来解决:
override init() {
super.init()
DispatchQueue.main.async { self.setup() }
}
如果您可以将
OtherManager
的刷新函数签名,即func refreshData()
更改为func refreshData(for:MyObject)
,则此方法非常快捷:
或者,尽管有点笨重(而且是Objective C风格),我还是会使用私有的共享
实例,并在其属性观察器中调用设置
static var shared: MyObject {
get {
if _shared == nil {
_shared = MyObject()
}
return _shared
}
}
private static var _shared: MyObject! = nil {
didSet {
shared.setup()
}
}
编辑:为什么DispatchQueue.main.async{self.setup()}
不是正确的解决方案:
class Test: NSObject {
var value: Int!
override init() {
super.init()
DispatchQueue.main.async {
self.setup()
}
}
func setup() {
value = 1
}
}
let test = Test()
// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION...
let value: Int = test.value
最后一行代码
let value:Int=test.value
在对象有机会调用setup之前被调用,这并不奇怪,因为它是async
调用。@Mocha当我运行p MyObject.shared
时,它打印错误:执行被中断,原因:EXC\u BAD\u指令(code=EXC\u I386\u INVOP,subcode=0x0)。进程已返回到表达式求值之前的状态。
这不是一个无限循环吗?如果执行让myObj=MyObject.shared
,它将调用init方法init()
在setup()
返回之前不会返回,这依赖于OtherStuff.refreshData()
。由于MyObject.shared
在refreshData()
引用它时尚未初始化,因此再次调用init()
,依此类推。我不确定你在这里要做什么,但是这种循环依赖和紧密耦合是很好的避免方法。@Drew Yeah是有意义的。那么,有没有一种方法只能在myObj
完全初始化并且安装完成后才调用setup
?E.com的答案可能是让您开始的最佳选择。还有Swift的defer
语句,它将在方法返回后执行一个块。但更一般地说,我建议您检查是否需要这两个对象以这种方式相互依赖,如果可能的话,设计一个更松散的耦合。从长远来看,您可能会发现这更易于维护。实际上,不要使用defer
。我不记得那是怎么回事<代码>延迟在当前作用域退出之前执行,并且不能导致范围内的更改,因此在此处不合适。对不起,有一秒钟我被调转了。如果可能的话,我真的很想避免这种情况。由于我的项目是如何设置的,这只会添加很多元素,我认为这会造成很多混乱和复杂性。不是说我最终不会采用这个解决方案。但我希望看到其他可能的解决方案,因为我正在寻找比这更干净的解决方案。从init()中删除setup(),然后首先调用MyObject.shared.setup(),以确保单例存在。这是对的吗?这看起来像是一种超级清洁的溶液。这将确保在调用setup
之前shared
始终可用?这并不是说这里有潜在的竞争条件或其他什么?好吧,如果你担心其他人可能会调用setup
,请将其设置为私有(或将其内容直接移动到那些花括号中)。否则我看不到比赛在哪里;只有一个主线程,它是串行的。这有点误导,async
是later
并且不保证在使用对象之前设置setup
。如果在init之后使用了MyObject
的实例,并且要求该实例已被setup
,则此解决方案将失败,因为setup块前面可能有几行紧跟在init
之后的代码。我将在下面的答案中添加一个操场测试来进一步证明这一点
class Test: NSObject {
var value: Int!
override init() {
super.init()
DispatchQueue.main.async {
self.setup()
}
}
func setup() {
value = 1
}
}
let test = Test()
// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION...
let value: Int = test.value