使用NSTimer安排多个日常活动?

时间:2010-04-13 20:33:06

标签: objective-c ios nstimer launchd cydia

我有一个存储在NSDictionary中的计划缓存。

对于下面的示例,我的计划时间为 20120年1月13日下午2:00 2012年1月13日下午2:05 。如何将这两个添加到队列中以自行触发?

构建计划缓存:

-(void) buildScheduleCache
{  
    NSCalendarDate *now = [NSCalendarDate calendarDate];

    NSFileManager *manager = [[NSFileManager defaultManager] autorelease];
    path = @"/var/mobile/Library/MobileProfiles/Custom Profiles";
    theProfiles = [manager directoryContentsAtPath:path];

    myPrimaryinfo = [[NSMutableArray arrayWithCapacity:6] retain];
    keys = [NSArray arrayWithObjects:@"Profile",@"MPSYear",@"MPSMonth",@"MPSDay",@"MPSHour",@"MPSMinute",nil];

    for (NSString *profile in theProfiles) 
    {
        plistDict = [[[NSMutableDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/%@",path,profile]] autorelease];

        [myPrimaryinfo addObject:[NSDictionary dictionaryWithObjects:
                                  [NSArray arrayWithObjects:
                                   [NSString stringWithFormat:@"%@",profile], 
                                   [NSString stringWithFormat:@"%@",[plistDict objectForKey:@"MPSYear"]], 
                                   [NSString stringWithFormat:@"%@",[plistDict objectForKey:@"MPSMonth"]], 
                                   [NSString stringWithFormat:@"%@",[plistDict objectForKey:@"MPSDay"]], 
                                   [NSString stringWithFormat:@"%@",[plistDict objectForKey:@"MPSHour"]], 
                                   [NSString stringWithFormat:@"%@",[plistDict objectForKey:@"MPSMinute"]],
                                   nil]forKeys:keys]];

        profileSched =
        [NSCalendarDate dateWithYear:[plistDict objectForKey:@"MPSYear"]
                               month:[plistDict objectForKey:@"MPSMonth"]
                                 day:[plistDict objectForKey:@"MPSDay"]
                                hour:[plistDict objectForKey:@"MPSHour"]
                              minute:[plistDict objectForKey:@"MPSMinute"]
                              second:01
                            timeZone:[now timeZone]];

        [self rescheduleTimer];
    }

    NSString *testPath = @"/var/mobile/Library/MobileProfiles/Schedules.plist";
    [myPrimaryinfo writeToFile:testPath atomically:YES];
}

安排活动:

-(void) scheduleProfiles
{
    NSFileManager *manager = [[NSFileManager defaultManager] autorelease];
    path = @"/var/mobile/Library/WrightsCS/MobileProfiles/Custom Profiles";
    theProfiles = [manager contentsOfDirectoryAtPath:path error:nil];

    for (NSString *profile in theProfiles) 
    {
        NSMutableDictionary * plistDict = [[[NSMutableDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/%@",path,profile]] autorelease];

        profileSched =
        [NSCalendarDate dateWithYear:[[plistDict objectForKey:@"MPSYear"] intValue]
                               month:[[plistDict objectForKey:@"MPSMonth"] intValue]
                                 day:[[plistDict objectForKey:@"MPSDay"] intValue]
                                hour:[[plistDict objectForKey:@"MPSHour"] intValue]
                              minute:[[plistDict objectForKey:@"MPSMinute"] intValue]
                              second:01
                            timeZone:[NSTimeZone localTimeZone]];


            NSLog(@"DATE: %@      SCHEDULE: %@      PROFILE: %@",[NSDate date],profileSched,profile);
        if([NSDate date] < profileSched)
        {
            NSLog(@"IGNORING PROFILE: %@     WITH SCHEDULE: %@",profile,profileSched);
        }else{
            //Create the timer from the Cached Array
            schedTimer = [[NSTimer alloc] initWithFireDate:profileSched //[NSDate dateWithTimeIntervalSinceNow: 10]
                                                  interval:0.1f
                                                    target:self
                                                  selector:@selector(fireCustomProfile:)
                                                  userInfo:profile
                                                   repeats:NO];//[[plistDict objectForKey:@"MPSRepeat"] boolValue]];

            MLogString(@"Scheduling Profile: %@",profile);
            [[NSRunLoop currentRunLoop] addTimer:schedTimer forMode:NSDefaultRunLoopMode];
        }
    }
}

点火活动:

-(void)fireCustomProfile:(NSTimer *)timer
{   
    if([[NSDate date] earlierDate:[schedTimer fireDate]])
    {
        NSLog(@"Ignoring Profile: %@",[schedTimer userInfo]);
        return;
    }

    notify_post("com.wrightscs.MobileProfiles.setCustomProfile");
}

示例事件:

<array>
    <dict>
        <key>MPSDay</key>
        <string>13</string>
        <key>MPSHour</key>
        <string>21</string>
        <key>MPSMinute</key>
        <string>15</string>
        <key>MPSMonth</key>
        <string>1</string>
        <key>MPSYear</key>
        <string>2012</string>
        <key>Profile</key>
        <string>Event 1</string>
        <key>Repeat</key>
        <true/>
    </dict>
</array>
<array>
    <dict>
        <key>MPSDay</key>
        <string>13</string>
        <key>MPSHour</key>
        <string>21</string>
        <key>MPSMinute</key>
        <string>20</string>
        <key>MPSMonth</key>
        <string>1</string>
        <key>MPSYear</key>
        <string>2012</string>
        <key>Profile</key>
        <string>Event 2</string>
        <key>Repeat</key>
        <true/>
    </dict>
</array>

3 个答案:

答案 0 :(得分:4)

您确定计时器是您想要的吗?请记住,计时器仅在您的应用处于活动状态时有效。当应用处于非活动状态时,它不起作用。如果你想让你的应用程序在遥远的未来随时做某事,计时器将无法真正解决你的问题,因为每次你退出应用程序时,它的所有计时器都会死掉。

假设(1)你只是想在应用程序处于活动状态时设置事件的计时器,(2)队列中总是有未知数量的事件,那么你最大的问题将是如何定位一个任意数量的事件的任意数量的计时器。

幸运的是,计时器可以传递userInfo属性中的任意对象,因此您希望将每个事件包装在一个对象中并将该对象传递给计时器。然后,计时器触发方法可以提取事件并对其进行操作。

这样的事情:

// assume a data model with event objects with the attributes and methods shown

- (void) setTimerForEvent:(EventClass *) anEvent{
    [NSTimer timerWithTimeInterval:[anEvent eventTimeFromNow] 
                            target:self 
                          selector:@selector(fireEvent:) 
                          userInfo:anEvent 
                           repeats:NO];

}//------------------------------------setTimerForEvent:------------------------------------

- (void)fireEvent:(NSTimer*)theTimer{
    [self handleEvent:(EventClass *)theTimer.userInfo]; 
    [theTimer invalidate];
}//------------------------------------fireEvent:------------------------------------

-[EventClass eventTimeFromNow]应该返回NSTimerInterval,它是调用方法和事件时间之间剩余的秒数。

答案 1 :(得分:3)

您可以使用UILocalNotification而不是NSTimer。限制是,如果没有您的交互,您的应用程序中就没有操作,但即使应用程序已关闭,也会触发通知。

关于UILocalNotifications的Apple文档:

  

UILocalNotification的实例表示通知   应用程序可以安排在特定时间向其用户进行演示   日期和时间。操作系统负责交付   在适当的时间通知;应用程序不必   为此而奔波。

     

...

     

当系统发送本地通知时,有几件事可以   发生,取决于应用程序状态和类型   通知。如果应用程序不是最前面且可见,那么   系统显示警报消息,标记应用程序并播放   声音 - 通知中指定的任何内容。如果是通知   是一个警报,用户点击操作按钮(或者,如果设备是   锁定,拖动打开动作滑块),启动应用程序。在   应用程序:didFinishLaunchingWithOptions:方法应用程序   委托可以从传入中获取UILocalNotification对象   使用选项字典   UIApplicationLaunchOptionsLocalNotificationKey键。代表可以   检查通知的属性,如果是通知   在其userInfo字典中包含自定义数据,它可以访问它   数据并相应地处理它。另一方面,如果是本地的   通知仅标记应用程序图标和用户   响应启动应用程序,   application:didFinishLaunchingWithOptions:方法被调用,但没有   UILocalNotification对象包含在选项字典中。

     

如果应用程序是最重要的,并且在系统交付时可见   通知,没有显示警报,没有图标标记,没有声音   玩过的。但是,应用程序:didReceiveLocalNotification:是   如果应用程序委托实现它,则调用它。该   UILocalNotification实例传递给这个方法,并且   委托可以检查其属性或访问来自的任何自定义数据   userInfo字典。

你可以找到一个好的教程here,主要的想法是这样的:

通知设置:

Class cls = NSClassFromString(@"UILocalNotification");
if (cls != nil) {
    UILocalNotification *notif = [[cls alloc] init];
    notif.fireDate = [datePicker date];
    notif.timeZone = [NSTimeZone defaultTimeZone];

    notif.alertBody = TEXT;
    notif.alertAction = LAUNCH_BUTTON;
    notif.soundName = UILocalNotificationDefaultSoundName;
    notif.applicationIconBadgeNumber = NOTIFICATIONS_REMAINING;

    NSDictionary *userDict = [NSDictionary dictionaryWithObject:CUSTOM_INFO 
                                            forKey:kRemindMeNotificationDataKey];
    notif.userInfo = userDict;

    [[UIApplication sharedApplication] scheduleLocalNotification:notif];
    [notif release];
}

在应用程序未运行时处理nofitication(在applyaction委托中):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    Class cls = NSClassFromString(@"UILocalNotification");
    if (cls) {
        UILocalNotification *notification = [launchOptions objectForKey:
                        UIApplicationLaunchOptionsLocalNotificationKey];

        if (notification) {
            NSString *reminderText = [notification.userInfo 
                        objectForKey:kRemindMeNotificationDataKey];
           [viewController showReminder:reminderText];
        }
    }

    application.applicationIconBadgeNumber = NOTIFICATIONS_REMAINING_LESS_ONE;

    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    return YES;
}

应用程序在前台(在应用程序代理中):

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {

    UIApplicationState state = [application applicationState];
    if (state == UIApplicationStateInactive) {
        // Application was in the background when notification
        // was delivered.
    }
}

答案 2 :(得分:0)

每个活动都需要一个计时器。 也许不要将计时器分配给timer变量。您可以创建这些计时器并在theFireEvent中释放它们。您需要将该选择器更改为@selector(theFireEvent),并确保该方法具有签名:

- (void)timerFireMethod:(NSTimer*)theTimer