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