注意:在编辑的更下方,有一些简单的代码会产生问题,而没有我的原始程序的全部复杂性。
我正在尝试为越狱的 iOS 编写一个闹钟应用程序。我有一个 UI 设置为一个独立的应用程序来安排警报,然后将警报信息保存到磁盘。保存文件由始终运行的启动守护程序读取,该守护程序处理实际调度警报。
我正在安排警报(编辑:在守护程序中)(NSDate *fireDate
之前计算过):
NSTimer *singleTimer = [[NSTimer alloc] initWithFireDate:fireDate
interval:0
target:self
selector:@selector(soundAlarm:)
userInfo:alarm
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:singleTimer
forMode:NSRunLoopCommonModes];
[self.timers addObject:singleTimer];
[singleTimer release];
编辑:上面的代码在一个名为 的方法中运行,该方法createTimers
由reloadData
. reloadData
从共享的保存文件中读取有关计时器的信息,并在AMMQRDaemonManager
的 init 函数中调用它,以及每当管理器收到notify_post
UI 应用程序已更新保存文件的通知时(使用 )。
soundAlarm:
方法(编辑:也在守护进程中)是:
- (void)soundAlarm:(NSTimer *)theTimer {
NSLog(@"qralarmdaemon: sounding alarm");
extern CFStringRef kCFUserNotificationAlertTopMostKey;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(dict, kCFUserNotificationAlertTopMostKey, kCFBooleanTrue);
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFSTR("Title"));
CFDictionaryAddValue(dict,kCFUserNotificationDefaultButtonTitleKey, CFSTR("OK"));
SInt32 err = 0;
CFUserNotificationRef notif = CFUserNotificationCreate(NULL,
0, kCFUserNotificationPlainAlertLevel, &err, dict);
CFOptionFlags response;
if((err) || (CFUserNotificationReceiveResponse(notif, 0, &response))) {
// do stuff
} else if((response & 0x3) == kCFUserNotificationDefaultResponse) {
// do stuff
}
CFRelease(dict);
CFRelease(notif);
// Do some other stuff
}
这很好用,并显示手机是否解锁或锁定的警报。但是,如果手机被锁定足够长的时间进入深度睡眠,那么计时器将无法触发。
我不需要它来打开屏幕(尽管那会很好),因为除了显示警报之外,我还会播放声音,但我确实需要触发计时器,以便我知道何时启动声音。
有任何想法吗?
编辑:这是main
守护进程的功能。
int main(int argc, char **argv, char **envp) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"qralarmdaemon: launched");
AMMQRDaemonManager *manager = [[AMMQRDaemonManager alloc] init];
NSTimer *keepRunningTimer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture]
interval:1000
target:manager
selector:@selector(keepRunning:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:keepRunningTimer
forMode:NSRunLoopCommonModes];
// Execute run loop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop run];
[manager release];
NSLog(@"qralarmdaemon: exiting");
[pool release];
return 0;
}
(不包括注册来自主应用程序的通知以了解何时读取保存文件等的代码,但我认为这不相关)。
编辑(再次):我在运行循环中添加了一个计时器,该计时器在[NSDate distantFuture]
. 这似乎可以延长计时器(在手机锁定后 1 分 45 秒安排的计时器关闭,并唤醒手机)但不是无限期地(在手机锁定后 7 分 30 秒安排的计时器没有关闭)。
编辑:我构建了以下玩具示例来说明问题,而不必担心与我的代码的其他部分的交互。
我编译了这段代码,通过 SSH 连接并运行它,然后锁定了我的手机。如果我将 更改dateByAddingTimeInterval:480
为dateByAddingTimeInterval:30
,我会得到以下输出:
2013-03-31 12:21:25.555 daemontimertest[6160:707] daemon-timer-test: launched
2013-03-31 12:21:56.265 daemontimertest[6160:707] daemon-timer-test: timer fired
但是当它设置为 480 时,我等了 8 多分钟,只看到第一行:
2013-03-31 12:08:09.331 daemontimertest[6049:707] daemon-timer-test: launched
main.m
:
#import "MyClass.h"
int main(int argc, char **argv, char **envp) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"daemon-timer-test: launched");
MyClass *obj = [[MyClass alloc] init];
NSTimer *singleTimer = [[NSTimer alloc] initWithFireDate:[[NSDate date] dateByAddingTimeInterval:480]
interval:0
target:obj
selector:@selector(fireTimer:)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:singleTimer
forMode:NSRunLoopCommonModes];
// Execute run loop
[[NSRunLoop currentRunLoop] run];
[pool release];
return 0;
}
MyClass.m
:
#import "MyClass.h"
@implementation MyClass
- (void)fireTimer:(NSTimer *)theTimer {
NSLog(@"daemon-timer-test: timer fired");
}
@end
编辑(3/31/13 5:50 EDT):我在玩具应用程序代码中添加了以下代码,以纳入 Nate 关于使用 GCDdispatch_after
功能的建议,但它似乎受到相同的时间限制。作为附加说明,主 UI 应用/Applications
程序安装在/usr/bin
.
double delayInSeconds = 10.0;
NSLog(@"daemon-timer-test: delay is %f",delayInSeconds);
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(@"daemon-timer-test: time has passed.");
});
编辑(3/31 5:54 PM):另一个快速说明。在系统日志似乎进入深度睡眠之前,以下几行(不连续)出现在系统日志中,并且在我唤醒手机之前没有更多消息。我选择了看起来可能相关的那些;最后一条消息是深度睡眠前发送到 syslog 的最后一条消息。
Mar 31 17:34:23 Andrew-MacKie-Masons-iPhone lockdownd[50]: 002c1000 -[hostWatcher handleSleepNotification:service:messageArgument:]: <hostWatcher: 0x1cd59890> [CC535EDB-0413-4E5E-A844-4DA035E7217C 169.254.2.141:54757] [fd=13]: kIOMessageCanSystemSleep
Mar 31 17:34:23 Andrew-MacKie-Masons-iPhone lockdownd[50]: 002c1000 -[hostWatcher handleSleepNotification:service:messageArgument:]: <hostWatcher: 0x1cd59890> [CC535EDB-0413-4E5E-A844-4DA035E7217C 169.254.2.141:54757] [fd=13]: kIOMessageSystemWillSleep
...
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone lockdownd[50]: 00343000 __63-[hostWatcher handleSleepNotification:service:messageArgument:]_block_invoke_0: Allowing Sleep
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone powerd[42]: PM scheduled RTC wake event: WakeImmediate inDelta=645.40
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone powerd[42]: Idle Sleep Sleep: Using BATT (Charge:76%)
...
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone kernel[0]: en0::stopOutputQueues
...
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone kernel[0]: pmu wake events: menu