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

发布于 2024-08-28 13:46:36 字数 5953 浏览 5 评论 0原文

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

对于下面的示例,我的计划时间为 20120 年 1 月 13 日下午 2:002012 年 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>

I have a schedule cache stored in an NSDictionary.

For the example below, I have a schedule time of January 13, 20120 2:00PM and January 13, 2012 2:05PM. How can I add both of these to a queue to fire on their own?

Build the Schedule Cache:

-(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];
}

Schedule The Event:

-(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];
        }
    }
}

Fire the Event:

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

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

Example Event:

<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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

彩虹直至黑白 2024-09-04 13:46:36

您确定计时器是您想要的吗?请记住,计时器仅在您的应用程序处于活动状态时才处于活动状态。当应用程序处于非活动状态时,它不起作用。如果您试图让您的应用程序在遥远的将来的任何时间执行某些操作,计时器并不能真正解决您的问题,因为每次您退出应用程序时,它的所有计时器都会消失。

假设 (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。

Are you sure a timer is what you want? Remember, a timer is only active when your app is active. It does not work when the app is inactive. If you're trying to get your app to do something at any time in the distant future, a timer will not really solve your problem because every time you quit the app, all its timers die.

Assuming that (1) you do just want to set timers for events while the app is active and (2) you always have an unknown number of events in the queue, then your biggest problem will be sorting out how to target an arbitrary number of timers for an arbitrary number of events.

Fortunately, timers can pass any arbitrary object in their userInfo property so you want to wrap every event in an object and pass that object to the timer. Then the timer fired method can extract the event and act on it.

Something like this:

// 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:------------------------------------

The -[EventClass eventTimeFromNow] should return a NSTimerInterval of the seconds remaining between the time the method is called and time of the event.

嗫嚅 2024-09-04 13:46:36

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

有关 UILocalNotifications

UILocalNotification 的实例表示通知
应用程序可以安排在特定的时间向用户展示
日期和时间。操作系统负责交付
在适当的时间发出通知;该应用程序不必
为实现这一目标而奔跑。

...

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

系统交付时应用程序是否位于最前面且可见
通知,不显示警报,没有图标标记,并且没有声音
已播放。但是, application:didReceiveLocalNotification: 是
如果应用程序委托实现它,则调用。这
UILocalNotification 实例被传递到此方法中,并且
委托可以检查其属性或访问任何自定义数据
userInfo 字典。

您可以在此处 主要思想是这样的:

通知设置:

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];
}

当应用程序未运行时处理通知(在应用程序委托中):

- (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.
    }
}

You can use UILocalNotification instead of NSTimer. The limitations are that there is no actions in your application without your interaction, but there is triggered a notification even if the app is shutted down.

Apple documentation about UILocalNotifications:

Instances of UILocalNotification represent notifications that an
application can schedule for presentation to its users at specific
dates and times. The operating system is responsible for delivering
the notification at the proper time; the application does not have to
be running for this to happen.

...

When the system delivers a local notification, several things can
happen, depending on the application state and the type of
notification. If the application is not frontmost and visible, the
system displays the alert message, badges the application, and plays a
sound—whatever is specified in the notification. If the notification
is an alert and the user taps the action button (or, if the device is
locked, drags open the action slider), the application is launched. In
the application:didFinishLaunchingWithOptions: method the application
delegate can obtain the UILocalNotification object from the passed-in
options dictionary by using the
UIApplicationLaunchOptionsLocalNotificationKey key. The delegate can
inspect the properties of the notification and, if the notification
includes custom data in its userInfo dictionary, it can access that
data and process it accordingly. On the other hand, if the local
notification only badges the application icon, and the user in
response launches the application, the
application:didFinishLaunchingWithOptions: method is invoked, but no
UILocalNotification object is included in the options dictionary.

If the application is foremost and visible when the system delivers
the notification, no alert is shown, no icon is badged, and no sound
is played. However, the application:didReceiveLocalNotification: is
called if the application delegate implements it. The
UILocalNotification instance is passed into this method, and the
delegate can check its properties or access any custom data from the
userInfo dictionary.

You can found a good tutorial here where the main idea is like this:

The notification setup:

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];
}

Handling the nofitication when the application is not running (in the applicaction delegate):

- (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;
}

Application in the foreground (in the applicaction delegate):

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

    UIApplicationState state = [application applicationState];
    if (state == UIApplicationStateInactive) {
        // Application was in the background when notification
        // was delivered.
    }
}
迷爱 2024-09-04 13:46:36

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

- (void)timerFireMethod:(NSTimer*)theTimer

You'll need a timer for each event.
Maybe don't assign the timer to the timer variable. You can just create these timers and release them in the theFireEvent. You'll need to change that selector to @selector(theFireEvent) though, and make sure the method has the signature:

- (void)timerFireMethod:(NSTimer*)theTimer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文