如何检测iPhone自上次应用启动以来是否已重新启动

6
我希望能够从我的应用程序内部检测iPhone自上次启动我的应用程序以来是否已重新启动。我需要这样做是因为我的应用程序使用自系统上次重启以来的计时器来计算用户的时间,我想检测重新启动以便使时间无效。
是否有任何地方可以从系统控制台日志中提取信息,例如重新启动、崩溃等?xcode中的组织者可以访问它,也许我也可以。
如果没有,你能想到其他获取此信息的方法吗?

是否有一种获取电话运营商时间的方法? - Maxm007
5个回答

15

这似乎是可行的:

  • 获取自上次重启以来的时间,对于此示例,让我们将其存储在一个名为“tslr”的变量中(我猜是毫秒为单位的持续时间,顺便问一下,如何获得?)
  • 获取当前时间,例如将其存储在变量'ct'中
  • 计算上次重启时间(我们称其为'lr'),我们有:lr = ct - tslr
  • 存储'lr'

下一次启动您的应用程序时,加载'lr'的先前值,计算新值,如果它们不同,则已检测到重启(您可能需要容忍一些差异...也许几毫秒)。

我认为这很难被欺骗...用户必须非常精确地篡改他们的手机时间,并且他们还必须在恰好相同的新'lr'出现的那一刻精确启动您的应用程序...相当困难,他们能够做到的概率非常接近于0。而且,您无需任何互联网连接就可以做到这一点...

只有以下情况下,新的'lr'才会与之前的相同:

  • 手机没有重新启动,并且时间没有更改
  • 时间被篡改,并且用户设法在精确的毫秒数启动您的应用程序,以欺骗您的算法(发生这种情况的可能性非常小)

太棒了。完美运行,同时检测到用户更改iPhone日期。现在我可以在没有网络连接的情况下使任何被篡改的用户得分无效。顺便说一句:您可以使用mach计时器获取自重启以来的时间: http://discussions.apple.com/thread.jspa?threadID=1632831 - Maxm007
嗯,这个机器计时器很奇怪。尽管我的iPhone整夜都开着,也没有重新启动,但计时器似乎已经重置了。 - Maxm007
这种方法似乎是一种可靠的获取系统正常运行时间的方式,而不需要使用私有API或系统调用:https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSProcessInfo_Class/#//apple_ref/occ/instp/NSProcessInfo/systemUptime - Sean Dawson
实际上,[[NSProcessInfo processInfo] systemUptime]由于以下原因不适用于此目的:https://dev59.com/hFfUa4cB1Zd3GeqPHFcS。CACurrentMediaTime()是一个更好的选择。 - Sean Dawson
但是如果“lr”与先前的不同,我怎么知道这是由于重新启动(使用应用程序的正常方式)还是他们篡改了系统时间(使用应用程序的异常方式)? - Code Farmer
即使没有重新启动,获取最后一次启动时间的值也会不同。使用 sysctlbyname("kern.boottime", &tv, &tvSize, nil, 0) 获取上次重启时间。 - Manish Punia

2
    // Returns true if device has rebooted since last time
    private func deviceRebootedSinceLastTime() -> Bool {
        let userDefaults = NSUserDefaults.standardUserDefaults()
        let systemUptime = NSProcessInfo.processInfo().systemUptime;
        let timeNow = NSDate().timeIntervalSince1970
        let dateOfLastReboot = NSDate(timeIntervalSince1970: timeNow-systemUptime)

        var didDeviceRebootSinceLastTime = false

        if let storedDateOfLastReboot:NSDate = userDefaults.objectForKey("deviceLastRebootDate") as? NSDate {

            if Int(dateOfLastReboot.timeIntervalSinceDate(storedDateOfLastReboot)) < 1 { //
                print("Reboot time didn't change - date: \(dateOfLastReboot)");
            }
            else {
                print("Reboot time has changed - from: \(storedDateOfLastReboot) to \(dateOfLastReboot)");
                didDeviceRebootSinceLastTime = true

            }
        }
        else {
            print("Reboot time is saved for the first time")
            didDeviceRebootSinceLastTime = true // first time we save value
        }

        userDefaults.setObject(dateOfLastReboot, forKey: "deviceLastRebootDate")
        userDefaults.synchronize() // don't forget this!!!

        return didDeviceRebootSinceLastTime;
    }

不要使用.synchronize()。根据苹果文档的说明,"这个方法是不必要的,不应该被使用"。 - Ashley Mills

1
Zoran的回答是正确的方法;这是没有网络连接的最接近的方法。(出于安全原因,无论是移动子系统还是系统日志都无法访问) 如果您想防止恶意用户生成假时间数据,请让某些中央服务器(或企业部署的可信任本地服务器)为您跟踪与时间相关的事件。

0

在我的情况下,那样做行不通,因为我不能依赖互联网连接,也不能依赖iPhone的时间,因为用户可能会篡改它。 - Maxm007
用户也可以篡改您的应用程序。您需要多安全? - Amok
用户无法轻易篡改我的应用程序,因为它已经数字签名。它绝对需要比在“设置”->“通用”->“时间”更改iPhone时间更安全。 - Maxm007

-1

这是我写的一个程序。它获取当前的GMT时间和自上次重启以来的时间,推算出设备上次重启的日期,并使用NSUserDefaults在内存中跟踪此日期。享受吧!

注意:由于您想要检查自上次应用程序启动以来的时间,因此您需要确保在每次启动应用程序时调用该方法。最简单的方法是在+(void)initialize {中调用下面的方法,然后在需要手动检查时也调用它。

#define nowInSeconds CFAbsoluteTimeGetCurrent()//since Jan 1 2001 00:00:00 GMT
#define secondsSinceDeviceRestart ((int)round([[NSProcessInfo processInfo] systemUptime]))
#define storage [NSUserDefaults standardUserDefaults]
#define DISTANCE(__valueOne, __valueTwo) ((((__valueOne)-(__valueTwo))>=0)?((__valueOne)-(__valueTwo)):((__valueTwo)-(__valueOne)))

+(BOOL)didDeviceReset {
    static BOOL didDeviceReset;
    static dispatch_once_t onceToken;
    int currentRestartDate = nowInSeconds-secondsSinceDeviceRestart;
    int previousRestartDate = (int)[((NSNumber *)[storage objectForKey:@"previousRestartDate"]) integerValue];
    int dateVarianceThreshold = 10;
    dispatch_once(&onceToken, ^{
        if (!previousRestartDate || DISTANCE(currentRestartDate, previousRestartDate) > dateVarianceThreshold) {
            didDeviceReset = YES;
        } else {
            didDeviceReset = NO;
        }
    });
    [storage setObject:@(currentRestartDate) forKey:@"previousRestartDate"];
    [storage synchronize];
    return didDeviceReset;
}

不要使用 -[NSUserDefaults synchronize]。根据苹果文档的说明,"这个方法是不必要的,不应该被使用"。 - Ashley Mills
我们的应用支持iOS6+,因此如果用户在短时间内进入后台(试图强制退出应用程序以绕过设备重置检测),我们需要进行同步。只有在iOS7之后,同步才变得不必要。请不要因为这样的事情而投反对票。 - Albert Renshaw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接