如何在Swift的静态函数中调用函数一次?

如何在Swift的静态函数中调用函数一次?,swift,Swift,代码如下所示,但我认为我们不能在Swift中使用dispatch_once令牌 我尝试使用lazy,但失败了,因为lazy需要在成员类中使用 “lazy”不能用于已经懒惰的全局对象 该类是静态的,除了静态变量外,没有可使用的变量 @objc public class LaunchPipeLineApi : NSObject { private static var shared = Applaunch_AppLaunchMetrics() @objc publ

代码如下所示,但我认为我们不能在Swift中使用dispatch_once令牌

我尝试使用lazy,但失败了,因为lazy需要在成员类中使用

“lazy”不能用于已经懒惰的全局对象

该类是静态的,除了静态变量外,没有可使用的变量

@objc public class LaunchPipeLineApi : NSObject {
    
    private static var shared = Applaunch_AppLaunchMetrics()
    
    @objc public static func markApplicationStartTime() {
        shared.applicationStartTime = Int64(1000*Date.init().timeIntervalSince1970);
    }

    @objc public static func markApplicationEndTime() {
        shared.applicationEndTime = Int64(1000*Date.init().timeIntervalSince1970);
    }

    @objc public static func markFirstFrameRenderEndTime() {
        shared.firstFrameRenderEndTime =  Int64(1000*Date.init().timeIntervalSince1970);
    }

    @objc public static func markFirstInteractiveTime() {
        shared.firstInteractiveTime =  Int64(1000*Date.init().timeIntervalSince1970);
    }
        
    @objc public static func startReportAppLaunchMetrics(report:(_:String,_:Data) ->()) {
//here,I want to call this report only once even the function caller do multiple calls.How can I do?
        do {
            report("AppLaunchMonitor",try shared.serializedData())
        } catch {}
    }
}

dispatch\u once
最常见的用法是某个共享实例的线程安全实例化。例如,考虑:

@implementation MyObject

+ (instancetype)sharedInstance {
    static MyObject *sharedInstance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });

    return sharedInstance;
}

@end
在Swift中,我们现在可以更简单地实现完全相同的行为:

class MyObject {
    static let shared = MyObject()
}
它提供了与Objective-C模式相同的线程安全实例化逻辑


在您的示例中,您有
static
属性
shared
,它已经享受了这种线程安全、只运行一次的模式。(不过,我会让它成为
let
而不是
var

因此,您已经有了线程安全的实例化逻辑。所以问题是,对于
serializedData
报告
,您是否真的需要这种
dispatch\u once
行为。你能把它放在
Applaunch\u AppLaunchMetrics
类的
init
方法中吗?这将是最简单的,摆脱棘手的问题

如果您确实想要
dispatch\u once
startReportAppLaunchMetrics
排序逻辑(即使
static
属性
shared
已经有了这种行为),您只需使用一些状态属性来跟踪它(然后将其与锁或GCD串行队列或其他任何东西同步)。例如:

static let lock = NSLock()
static var hasRun = false

static func thisCanBeCalledMultipleTimesFromMultipleThreadsButRunOnlyOnce() {
    lock.lock()
    defer { lock.unlock() }

    if !hasRun {
        hasRun = true

        // do something
    }
}


因此,如果绝对需要,您可以这样做,但我不禁要问,鉴于您的
共享
已经享受到线程安全、一次调度,以及所有
静态
属性都享受的那种行为,您真的需要在
序列化数据的
报告中这样做吗,也是?

您到底想做什么?不能对静态变量或全局变量使用lazy,因为它们总是懒惰的。错误就是这么说的。@Sulthan我是swift新手,我只是添加了完整的code@Rob我只是更新了完整的codeUnrelated,但是如果
LaunchPipeLineApi
有一个名为
shared
的属性,按照惯例,人们会假设在没有任何限定符的情况下,共享的
将是
LaunchPipeLineApi
的实例,而不是
Applaunch\u AppLaunchMetrics
的实例。每当您看到
共享的
静态
属性本身时,它总是该类的实例,而不是某个其他类的实例。如果您真的想让它成为其他类型,我可以将其称为
sharedMetrics
,或者将其移动到
Applaunch\u AppLaunchMetrics
,以解决这种歧义。(另外,我会将那些
Date.init()
引用替换为
Date()
)好的。但是如果调用方调用了
startReportAppLaunchMetrics
,而它根本没有调用
报告
闭包,那么调用方可能会非常困惑。在呼叫点,这将非常难以解释。我可能会将其更改为将闭包调用为
(String?、Data?、Error?->Void
闭包或
(Result)->Void
,并确保不管如何都使用
字符串/Data
Error
调用它。或者让它抛出
错误,只返回
(字符串,数据)
(因为在这个上下文中有一个非转义闭包有点奇怪)。但是有一个有时调用有时不调用的闭包(也有非转义闭包)可能会引起混淆。当我们提供一个闭包时,你希望它被调用。我同意@Rob的说法,这种行为令人困惑@如果使用这种方法,func可能会返回一个成功/失败bool,或者如果
hasRun
已经为真,则
throw
会返回一个错误。然后调用方将有一些小机会知道在调用此方法时闭包没有运行。在我看来,仍然是一段代码,但是一些信息可以让我们深入了解行为,这可能比没有信息要好。我在这个函数上设置了一个断点,它崩溃了一次,所以我开始认为这个函数不是线程安全的,所以我要让它成为线程安全的,就像我在ObjC中使用分派一样。此外,在应用程序生命周期中,应该只调用一次闭包。无论如何,谢谢你的建议,我会再考虑一下。
static let synchronizationQueue = DispatchQueue(label: "synchronizationQueue")
static var hasRun = false

static func thisCanBeCalledMultipleTimesFromMultipleThreadsButRunOnlyOnce() {
    synchronizationQueue.async {
        if !hasRun {
            hasRun = true

            // do something
        }
    }
}