Ios 单例初始化后立即运行Swift代码

Ios 单例初始化后立即运行Swift代码,ios,swift,Ios,Swift,我有一些Swift代码,我想在我的singleton初始化后立即运行。此代码需要在我的单例初始化后运行的原因是,代码依赖于现有的单例 下面是我的目标代码示例。但这段代码似乎不起作用,因为refreshData试图在单例完全创建之前访问它 class MyObject: NSObject { static let shared: MyObject = MyObject() let otherManager: OtherStuff = OtherStuff() var n

我有一些Swift代码,我想在我的singleton初始化后立即运行。此代码需要在我的单例初始化后运行的原因是,代码依赖于现有的单例

下面是我的目标代码示例。但这段代码似乎不起作用,因为
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