关闭

NSTimer和NSNotification小结

标签: iosNSTimer通知Notification传参
1419人阅读 评论(2) 收藏 举报
分类:

最近在写的一段代码,是由某个组件执行一系列初始化脚本,每个脚本完成后,通知Controller刷新进度条。全部完成以后,通知Controller跳转

其中用到了模板方法模式,在另一篇博客里已经总结过了:在objective-c中实现模板方法模式

另外,原来写的代码有问题,类似:

for(int i = 0; i<[scripts count]; i++){
    // 依次调用脚本的doInit()方法
}

-(void) doInit
{
    // 逻辑处理
    // 通知Controller刷新进度条
}

这段代码没有成功,不知道为什么进度条毫无反应,等到全部脚本都执行完以后,才会一次性刷到头。在网上搜索了一番,发现用NSTimer类设置一个延时就可以解决这个问题。另外消息传递的方式,也从直接调用方法,改成了IOS中的Notification方式。本文总结一下这2方面的内容,以后可以直接拷贝继续用

NSTimer

直接上代码,再解释一下参数:

- (void) runInitScripts
{
    YLSClientInfo *clientInfo = [YLSClientInfo new];
    // 用NSTimer来延时执行,否则Bootstrap View Controller里的进度条刷新失败,原因不明
    [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(doRun:) userInfo:clientInfo repeats:YES];
}

-(void) doRun:(NSTimer*) timer
{
    if(doneSteps < maxSteps){
        [[scripts objectAtIndex:doneSteps] doInit:timer.userInfo];// 脚本未执行完,执行下一个脚本
    }else{
        // 脚本执行完毕,注销Timer,发送初始化完成通知
        [timer invalidate];
    }
}

代码包括2部分,scheduledTimerWithTimeInterval方法是注册一个定时任务,类似setTimeInterval(),其中参数的含义如下:

interval:延时,单位是秒,如果小于或等于0,会被设置为0.1

target:触发时调用哪个实例上的方法,一般就设置为self

selector:触发时调用的方法。这个方法的声明有讲究,后面会说

userInfo:如果需要传参,就写在这个参数里

repeats:如果设置为YES,就会反复执行,直到被注销;否则只执行一次


然后需要定义一个函数,来被前面的Timer触发,也就是第3个参数。这个函数返回值必须是void,只能接受一个NSTimer的指针作为参数

实际被调用时,这个参数会是Timer自己,然后调用timer.userInfo,就可以拿到设置Timer时的第4个参数。不需要做类型转换

通知机制

IOS中的通知是通过NSNotificationCenter这个类实现的,示例代码如下。下文的“事件”和“通知”是同义词

首先是注册通知侦听器:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listenStepDoneEvent:) name:STEP_DONE object:initialOperator];

参数的含义如下:

observer:事件发生时,调用哪个实例上的方法,一般就是self

selector:事件发生时,将会调用的函数。和NSTimer一样,这个函数的定义也是有要求的,后面会说

name:事件的名称,就是一个字符串,比如@"someEvent"这样,当这个事件发生时,才会触发侦听器

object:发送事件的实例,可以设置为nil。如果设置为nil,那么无论什么实例发的通知,都会被响应;否则只有设置的对象发的通知才会被响应。网上很多文章都把这个参数当成selector函数的参数,其实不是的,可能API变过


注册了通知侦听器,下面是发送通知的代码,已经考虑了需要传参的场景:

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
                          stepMsg, @"content", [NSNumber numberWithFloat:currentProgress], @"stage", nil];// KEY在后面,VALUE在前面
[[NSNotificationCenter defaultCenter] postNotificationName:STEP_DONE object:self userInfo:dict];

第二行才是发送通知的代码,但是为了要传参,在第一行组织了一下参数。各参数的含义是:

name:该通知的名称,和注册侦听器时的name参数对应

object:谁发送的通知,一般都设置成self,和注册侦听器时的object参数对应

userInfo:如果要传参,就设置为要传的参数;如果不需要传参,可以设置为nil

上面的userInfo似乎和NSTimer里很像,不过还是有点区别,这里的userInfo必须是NSDictionary类型,而Timer里的userInfo可以是任意类型。另外,组织NSDictionary对象时,注意KEY在后面,VALUE在前面。。。这和javascript与java里的API是相反的


然后是事件触发时要调用的函数定义:

// 响应单个初始化脚本完成事件
-(void) listenStepDoneEvent:(NSNotification*) notification
{
    NSNumber* stage = [notification.userInfo objectForKey:@"stage"];
    NSString* content = [notification.userInfo objectForKey:@"content"];
    [self refreshProgressTo:[stage floatValue] WithContent:content];
}

这个函数是在注册侦听器的时候的第2个参数,返回值必须是void,接收一个NSNotification类型的参数。通过notification.userInfo,可以取到发送通知时设置的userInfo,通过这种方式来传参。不过前面说过,userInfo的类型规定是NSDictionary,不能是自定义类型,所以没有Timer的方法那么方便


最后是注销侦听器的方法,记得在最后合适的地方要调用一下:

[[NSNotificationCenter defaultCenter] removeObserver:self];// 初始化完成,移除所有Listener

这个方法会移除self的所有侦听器,还有一个更细粒度的API,可以指定移除对具体某个事件,或者某个来源的侦听,文档里有

1
1

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:761915次
    • 积分:14056
    • 等级:
    • 排名:第905名
    • 原创:651篇
    • 转载:18篇
    • 译文:0篇
    • 评论:130条
    最新评论