Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/120.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
本地本机iOS时间防黑客背景倒计时计时器_Ios_Objective C_Swift_Timer - Fatal编程技术网

本地本机iOS时间防黑客背景倒计时计时器

本地本机iOS时间防黑客背景倒计时计时器,ios,objective-c,swift,timer,Ios,Objective C,Swift,Timer,我想用一种方法来创建一个本地本地的iOS时间防黑客背景倒计时计时器,例如在游戏中为下一个奖励倒计时 对象说明: 本地 -在没有互联网连接的情况下工作 本机iOS -我想在(最好)Objective-C或Swift中找到解决方案 防时间黑客攻击 -当用户向前/向后更改其设备时间时,剩余时间保持不变 背景 -关闭/重新打开 倒计时 -一种定时器,在启动后提供检查剩余时间的方法 我希望在我的iOS游戏中有一个基于时间的奖励系统,它不容易通过将设备时间向前移动来破解 最初,我认为这是不可能的,并一直使用

我想用一种方法来创建一个本地本地的iOS时间防黑客背景倒计时计时器,例如在游戏中为下一个奖励倒计时

对象说明:

本地
-在没有互联网连接的情况下工作

本机iOS
-我想在(最好)Objective-C或Swift中找到解决方案

防时间黑客攻击
-当用户向前/向后更改其设备时间时,剩余时间保持不变

背景
-关闭/重新打开

倒计时
-一种定时器,在启动后提供检查剩余时间的方法

我希望在我的iOS游戏中有一个基于时间的奖励系统,它不容易通过将设备时间向前移动来破解

最初,我认为这是不可能的,并一直使用
[NSDate date]
savedDate
,将它们一起比较以找出经过的时间。然而,最近我遇到了一款使用此功能的游戏:Crossy Road。据我所知,这是一款Unity游戏,但我怀疑编译成iOS应用程序的Unity project中提供的类似此功能在iOS上无法通过Objective-C或Swift访问


他们提供了一个定时奖励系统,每6小时一次,而且,据我测试,它不能通过改变设备时间来破解。它也可以在没有互联网连接的情况下工作。(如果你不连接,我不确定它是否有效,但当我连接、断开连接并尝试使用黑客时,它不起作用。)

你可以使用当前系统正常运行时间(
mach\u absolute\u time()
)。使用
CACurrentMediaTime()
可以轻松访问该函数,它可以在几秒钟内轻松返回该函数。然后您可能需要设置某种间隔来检查该值。当然,这可以使用NSDate来完成,因为用户将时钟向前设置触发它不是问题。重新启动时,只需保存启动值,并在重新启动后将其用作偏移量

唯一的缺点是,它不计算设备关闭的时间,但在现在没有人打开手机的情况下,这通常不是什么大问题


要获得实际的计时器功能,只需在开始时保存该值,然后定期对照预计的结束值进行检查,随着结束的临近,可能会增加分辨率。

我对正在构建的应用程序有着完全相同的要求。我尝试使用caccurrentmediatime(),但当您关闭设备时,它会重置为0,这不是您想要的。即使在关闭设备的情况下,也可以使用此时间参考:

currentUser.timeLastUpdated = [[NSDate date] timeIntervalSince1970];
下面是帮助您实现计时器的代码。这是从另一个stackoverflow线程修改的

在MainViewController.m中

@interface MainViewController ()

@property (strong, nonatomic) IBOutlet UILabel *countdownLabel;

@end

@implementation MainViewController
{
    UserData *_currentUser;
    int hours;
    int minutes;
    int seconds;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _currentUser = [UserData currentUser];
    [self updateTimerText];
    [self startTimer];
}

- (void)startTimer {
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
}

- (void)updateTimer:(NSTimer *)timer {
    if (_currentUser.secondsLeft > 0) {
        _currentUser.secondsLeft--;
        [self updateTimerText];
    }
}

- (void)updateTimerText {
    int secondsLeft = _currentUser.secondsLeft;
    hours = secondsLeft / 3600;
    minutes = (secondsLeft%3600) / 60;
    seconds = (secondsLeft%3600) % 60;
    self.countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
}

}
@implementation AppDelegate
{
    UserData *currentUser;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    // Intialize currentUser
    currentUser = [UserData currentUser];

    // Reset secondsLeft based on lastTimeUpdated
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
        currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
    }

    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    // Set the last time updated (only need to call it here because this is coupled with applicationWillTerminate
    currentUser.lastTimeUpdated = (int)[[NSDate date] timeIntervalSince1970];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

    // Reset secondsLeft based on lastTimeUpdated
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
        currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
    }

}
-(void)setSecondsLeft:(int)secondsLeft {
    _secondsLeft = secondsLeft;
    [[NSUserDefaults standardUserDefaults] setObject:@(secondsLeft) forKey:@"secondsLeft"];
}

-(void)setLastTimeUpdated:(int)lastTimeUpdated {
    _lastTimeUpdated = lastTimeUpdated;
    [[NSUserDefaults standardUserDefaults] setObject:@(lastTimeUpdated) forKey:@"lastTimeUpdated"];
现在,要使其防弹,以便在停止应用程序时不会停止计数,您需要修改AppDelegate.m

在AppDelegate.m中

@interface MainViewController ()

@property (strong, nonatomic) IBOutlet UILabel *countdownLabel;

@end

@implementation MainViewController
{
    UserData *_currentUser;
    int hours;
    int minutes;
    int seconds;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _currentUser = [UserData currentUser];
    [self updateTimerText];
    [self startTimer];
}

- (void)startTimer {
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
}

- (void)updateTimer:(NSTimer *)timer {
    if (_currentUser.secondsLeft > 0) {
        _currentUser.secondsLeft--;
        [self updateTimerText];
    }
}

- (void)updateTimerText {
    int secondsLeft = _currentUser.secondsLeft;
    hours = secondsLeft / 3600;
    minutes = (secondsLeft%3600) / 60;
    seconds = (secondsLeft%3600) % 60;
    self.countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
}

}
@implementation AppDelegate
{
    UserData *currentUser;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    // Intialize currentUser
    currentUser = [UserData currentUser];

    // Reset secondsLeft based on lastTimeUpdated
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
        currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
    }

    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    // Set the last time updated (only need to call it here because this is coupled with applicationWillTerminate
    currentUser.lastTimeUpdated = (int)[[NSDate date] timeIntervalSince1970];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

    // Reset secondsLeft based on lastTimeUpdated
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
        currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
    }

}
-(void)setSecondsLeft:(int)secondsLeft {
    _secondsLeft = secondsLeft;
    [[NSUserDefaults standardUserDefaults] setObject:@(secondsLeft) forKey:@"secondsLeft"];
}

-(void)setLastTimeUpdated:(int)lastTimeUpdated {
    _lastTimeUpdated = lastTimeUpdated;
    [[NSUserDefaults standardUserDefaults] setObject:@(lastTimeUpdated) forKey:@"lastTimeUpdated"];
您只需要修改AppDelegate中的三个方法,因为ApplicationWillTerminate总是使用ApplicationIdentinterBackground激发(据我所知)

currentUser.secondsLeft和currentUser.LastTimeUpdate是整数。我将[[NSDate date]timeIntervalSince1970]转换为int,因为它返回一个CFTimeInterval,它是double的typealias

最后,您必须确保将其保存到数据库中。每当更新这两个变量时,我会通过在我的UserData.m文件中包含以下代码,将它们惰性地保存到NSUserDefaults:

在UserData.m中

@interface MainViewController ()

@property (strong, nonatomic) IBOutlet UILabel *countdownLabel;

@end

@implementation MainViewController
{
    UserData *_currentUser;
    int hours;
    int minutes;
    int seconds;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _currentUser = [UserData currentUser];
    [self updateTimerText];
    [self startTimer];
}

- (void)startTimer {
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
}

- (void)updateTimer:(NSTimer *)timer {
    if (_currentUser.secondsLeft > 0) {
        _currentUser.secondsLeft--;
        [self updateTimerText];
    }
}

- (void)updateTimerText {
    int secondsLeft = _currentUser.secondsLeft;
    hours = secondsLeft / 3600;
    minutes = (secondsLeft%3600) / 60;
    seconds = (secondsLeft%3600) % 60;
    self.countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
}

}
@implementation AppDelegate
{
    UserData *currentUser;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    // Intialize currentUser
    currentUser = [UserData currentUser];

    // Reset secondsLeft based on lastTimeUpdated
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
        currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
    }

    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    // Set the last time updated (only need to call it here because this is coupled with applicationWillTerminate
    currentUser.lastTimeUpdated = (int)[[NSDate date] timeIntervalSince1970];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

    // Reset secondsLeft based on lastTimeUpdated
    if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
        currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
    }

}
-(void)setSecondsLeft:(int)secondsLeft {
    _secondsLeft = secondsLeft;
    [[NSUserDefaults standardUserDefaults] setObject:@(secondsLeft) forKey:@"secondsLeft"];
}

-(void)setLastTimeUpdated:(int)lastTimeUpdated {
    _lastTimeUpdated = lastTimeUpdated;
    [[NSUserDefaults standardUserDefaults] setObject:@(lastTimeUpdated) forKey:@"lastTimeUpdated"];

这样,当应用程序退出时,数据会保存起来供以后使用。

我喜欢这个解决方案!但是如果你能计算总时间,加上“关闭”的时间,情况会好得多。。你知道怎么做吗?@Gyfis你有没有试过在离线状态下关掉手机,看看Unity游戏是否能继续进行下去?这可能是他们在离线游戏中采用的解决方案。是的,我试过了!令人惊讶的是,即使在我的手机关机的时候,Unity游戏中的时间也算对了!我想现在它变得非常有趣了!这是个好问题。你试过离线然后“作弊”吗?这可能是因为他们只需定期与远程服务器同步时钟。该设备实际上只有两个时钟,正常运行时间和“实时”时间(用户可以更改)。@Rick我认为建议的组合非常接近,我喜欢它!谢谢你的回答和评论:)设置你自己的参考。。加载时,若未设置时间戳,则将其保存到数据库(以便记录第一次运行的时间)。根据参考点计算时间,不管实际时间是多少。这对作弊有什么作用?我理解将第一个时间戳保存到数据库中,但什么样的防黑客方法可以获得“当前时间”以获得差异?