久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    NSTimer和實(shí)現(xiàn)弱引用的timer的方式

     最初九月雪 2018-03-30

    NikonPicTNW.jpg

    本文是投稿文章,作者:yohunl 


    目錄

    • 我們常用NSTimer的方式

    • 上面的NSTimer無(wú)論采用何種方式都是在主線程上跑的那么怎么在非主線程中跑一個(gè)NSTimer呢

    • GCD的方式

    • 一次性的timer方式的GCD模式

    • 另一種dispatch_after方式的定時(shí)器

    • 利用GCD的弱引用型的timer

    • 使用NSTimer方式創(chuàng)建的Timer使用時(shí)候需要注意

    • 參考文檔

    • 我們常用NSTimer的方式

    如下代碼所示,是我們最常見的使用timer的方式

    1
    2
    3
    4
    5
    @property (nonatomic , strong) NSTimer *animationTimer;self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(self.animationDuration = animationDuration)
                                                                   target:self
                                                                 selector:@selector(animationTimerDidFired:)
                                                                 userInfo:nil
                                                                  repeats:YES];123456

    當(dāng)使用NSTimer的scheduledTimerWithTimeInterval方法時(shí)。事實(shí)上此時(shí)Timer會(huì)被加入到當(dāng)前線程的Run Loop中,且模式是默認(rèn)的NSDefaultRunLoopMode。而如果當(dāng)前線程就是主線程,也就是UI線程時(shí),某些UI事件,比如UIScrollView的拖動(dòng)操作,會(huì)將Run Loop切換成NSEventTrackingRunLoopMode模式,在這個(gè)過(guò)程中,默認(rèn)的NSDefaultRunLoopMode模式中注冊(cè)的事件是不會(huì)被執(zhí)行的。也就是說(shuō),此時(shí)使用scheduledTimerWithTimeInterval添加到Run Loop中的Timer就不會(huì)執(zhí)行。
    我們可以通過(guò)添加一個(gè)UICollectionView,然后滑動(dòng)它后打印定時(shí)器方法

    1
    2
    3
    4
    5
    6
    7
    2016-01-27 11:41:59.770 TimerAbout[89719:1419729] enter timer
    2016-01-27 11:42:00.339 TimerAbout[89719:1419729] enter timer
    2016-01-27 11:42:01.338 TimerAbout[89719:1419729] enter timer
    2016-01-27 11:42:02.338 TimerAbout[89719:1419729] enter timer
    2016-01-27 11:42:03.338 TimerAbout[89719:1419729] enter timer
    2016-01-27 11:42:15.150 TimerAbout[89719:1419729] enter timer
    2016-01-27 11:42:15.338 TimerAbout[89719:1419729] enter timer

    從中可以看到,當(dāng)UICollectionView滑動(dòng)時(shí)候,定時(shí)器方法并沒有打印(從03.338到15.150)

    為了設(shè)置一個(gè)不被UI干擾的Timer,我們需要手動(dòng)創(chuàng)建一個(gè)Timer,然后使用NSRunLoop的addTimer:forMode:方法來(lái)把Timer按照指定模式加入到Run Loop中。這里使用的模式是:NSRunLoopCommonModes,這個(gè)模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的結(jié)合,官方參考文檔

    還是上面的例子,換為:

    1
    2
    self.animationTimer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(animationTimerDidFired:) userInfo:nil repeats:YES];
        [[NSRunLoop mainRunLoop] addTimer:self.animationTimer forMode:NSRunLoopCommonModes];12

    則,無(wú)論你滑動(dòng)不滑動(dòng)UICollectionView,定時(shí)器都是起作用的!

    上面的NSTimer無(wú)論采用何種方式,都是在主線程上跑的,那么怎么在非主線程中跑一個(gè)NSTimer呢?

    我們簡(jiǎn)單的可以使用如下代碼:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //創(chuàng)建并執(zhí)行新的線程
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
        [thread start];
         
    - (void)newThread
    {
        @autoreleasepool
        {        //在當(dāng)前Run Loop中添加timer,模式是默認(rèn)的NSDefaultRunLoopMode
            [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(animationTimerDidFired:) userInfo:nil repeats:YES];        //開始執(zhí)行新線程的Run Loop
            [[NSRunLoop currentRunLoop] run];
        }
    }1234567891011121314

    當(dāng)然了,因?yàn)槭情_啟的新的線程,在定時(shí)器的回調(diào)方法中,需要切換到主線程才能操作UI。

    GCD的方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //GCD方式
        uint64_t interval = 1 * NSEC_PER_SEC;
        //創(chuàng)建一個(gè)專門執(zhí)行timer回調(diào)的GCD隊(duì)列
        dispatch_queue_t queue = dispatch_queue_create("timerQueue", 0);
        _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        //使用dispatch_source_set_timer函數(shù)設(shè)置timer參數(shù)
        dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
        //設(shè)置回調(diào)
        dispatch_source_set_event_handler(_timer, ^(){
            NSLog(@"Timer %@", [NSThread currentThread]);
        });
        dispatch_resume(_timer);//dispatch_source默認(rèn)是Suspended狀態(tài),通過(guò)dispatch_resume函數(shù)開始它123456789101112

    其中的dispatch_source_set_timer的最后一個(gè)參數(shù),是最后一個(gè)參數(shù)(leeway),它告訴系統(tǒng)我們需要計(jì)時(shí)器觸發(fā)的精準(zhǔn)程度。所有的計(jì)時(shí)器都不會(huì)保證100%精準(zhǔn),這個(gè)參數(shù)用來(lái)告訴系統(tǒng)你希望系統(tǒng)保證精準(zhǔn)的努力程度。如果你希望一個(gè)計(jì)時(shí)器每5秒觸發(fā)一次,并且越準(zhǔn)越好,那么你傳遞0為參數(shù)。另外,如果是一個(gè)周期性任務(wù),比如檢查email,那么你會(huì)希望每10分鐘檢查一次,但是不用那么精準(zhǔn)。所以你可以傳入60,告訴系統(tǒng)60秒的誤差是可接受的。他的意義在于降低資源消耗。

    一次性的timer方式的GCD模式

    1
    2
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        NSLog(@"dispatch_after enter timer");
        });123

    另一種dispatch_after方式的定時(shí)器

    這個(gè)是使用上面的dispatch_after來(lái)創(chuàng)建的,通過(guò)遞歸調(diào)用來(lái)實(shí)現(xiàn)。

    1
    2
    3
    4
    5
    6
    7
    - (void)dispatechAfterStyle {
        __weak typeof (self) wself = self;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"dispatch_after enter timer,thread = %@", [NSThread currentThread]);
            [wself dispatechAfterStyle];
        });
    }

    利用GCD的弱引用型的timer

    MSWeaker實(shí)現(xiàn)了一個(gè)利用GCD的弱引用的timer。原理是利用一個(gè)新的對(duì)象,在這個(gè)對(duì)象中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    NSString *privateQueueName = [NSString stringWithFormat:@"com.mindsnacks.msweaktimer.%p", self];
            self.privateSerialQueue = dispatch_queue_create([privateQueueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
            dispatch_set_target_queue(self.privateSerialQueue, dispatchQueue);
            self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
                                                0,
                                                0,
                                                self.privateSerialQueue);
                                                 
    - (void)resetTimerProperties
    {
        int64_t intervalInNanoseconds = (int64_t)(self.timeInterval * NSEC_PER_SEC);
        int64_t toleranceInNanoseconds = (int64_t)(self.tolerance * NSEC_PER_SEC);
        dispatch_source_set_timer(self.timer,
                                  dispatch_time(DISPATCH_TIME_NOW, intervalInNanoseconds),
                                  (uint64_t)intervalInNanoseconds,
                                  toleranceInNanoseconds
                                  );
    }
    - (void)schedule
    {
        [self resetTimerProperties];
         
        __weak MSWeakTimer *weakSelf = self;
         
        dispatch_source_set_event_handler(self.timer, ^{
            [weakSelf timerFired];
        });
         
        dispatch_resume(self.timer);
    }

    創(chuàng)建了一個(gè)隊(duì)列self.timer = dispatch_source_create,然后在這個(gè)隊(duì)列中創(chuàng)建timer dispatch_source_set_timer.

    注意其中用到了dispatch_set_target_queue(self.privateSerialQueue, dispatchQueue); 這個(gè)是將dispatch隊(duì)列的執(zhí)行操作放到隊(duì)列dispatchQueue 中去。

    這份代碼中還用到了原子操作!值得好好研讀,以便以后可以在自己的多線程設(shè)計(jì)中使用原子操作。
    為什么用原子操作呢,因?yàn)樽髡呦氲氖窃诙嗑€程的環(huán)境下設(shè)置定時(shí)器的開關(guān)與否。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if (OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsInvalidated))
    if (!OSAtomicTestAndSet(7, &_timerFlags.timerIsInvalidated))
        {
            dispatch_source_t timer = self.timer;
            dispatch_async(self.privateSerialQueue, ^{
                dispatch_source_cancel(timer);
                ms_release_gcd_object(timer);
            });
        }

    至于其中

    1
    2
    3
    4
     struct
        {
            uint32_t timerIsInvalidated;
        } _timerFlags;

    這里為什么要用結(jié)構(gòu)體呢?為什么不直接使用一個(gè)uint32_t 的變量?

    使用NSTimer方式創(chuàng)建的Timer,使用時(shí)候需要注意。

    由于

    1
    2
    3
    4
    5
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1
                                                               target:self
                                                             selector:@selector(animationTimerDidFired:)
                                                             userInfo:nil
                                                              repeats:YES];

    會(huì)導(dǎo)致timer 強(qiáng)引用 self,而animationTimer又是self的一個(gè)強(qiáng)引用,這造成了強(qiáng)引用的循環(huán)了。 
    如果不手工停止timer,那么self這個(gè)VC將不能夠被釋放,尤其是當(dāng)我們這個(gè)VC是push進(jìn)來(lái)的時(shí)候,pop將不會(huì)被釋放!!! 
    怎么解決呢?
    當(dāng)然了,可以采用上文提到的MSWeakerGCD的弱引用的timer

    可是如果有時(shí)候,我們不想使用它,覺得它有點(diǎn)復(fù)雜呢?

    1.在VC的disappear方法中應(yīng)該調(diào)用 invalidate方法,將定時(shí)器釋放掉,這里可能有人要說(shuō)了,我直接在vc的dealloc中釋放不行么?

    1
    2
    3
    -(void)dealloc {
    [_animationTimer invalidate];
    }

    很遺憾的告訴你,都已經(jīng)循環(huán)引用了,vc壓根就釋放不了,怎么調(diào)dealloc方法?

    在vc的disappear方法中

    1
    2
    3
    4
    -(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [_animationTimer invalidate];
    }

    這樣的確能解決問(wèn)題,可是不一定是我們想要的呀,當(dāng)我們vc 再push了一個(gè)新的頁(yè)面的時(shí)候,本身vc沒有釋放,按理說(shuō),其成員timer不應(yīng)該被釋放呀,你可能會(huì)說(shuō),那還不容易,在appear方法中再重新生成一下唄…但是這樣的話,又要增加一個(gè)變量,標(biāo)識(shí)定時(shí)器在上一次disappear時(shí)候是不是啟動(dòng)了吧,是啟動(dòng)了,被invaliate的時(shí)候,才能在appear中重新啟動(dòng)吧。這樣是不是覺得很麻煩?

    3.你可能會(huì)說(shuō),那簡(jiǎn)單啊,直接若引用就可以了想想我們使用block的時(shí)候

    1
    2
    3
    4
    5
    @property (nonatomic, copy) void  (^ myblock)(NSInteger i);
    __weak typeof (self) weakSelf = self;
    self.myblock = ^(NSInteger i){
        [weakSelf view];
    };

    在其中,我們需要在block中引用self,如果直接引用,也是循環(huán)引用了,采用先定義一個(gè)weak變量,然后在block中引用weak對(duì)象,避免循環(huán)引用 你會(huì)直接想到如下的方式

    1
    2
    3
    4
    5
    6
    __weak typeof (self) wself = self;
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1
                                                           target:wself
                                                         selector:@selector(animationTimerDidFired:)
                                                         userInfo:nil
                                                          repeats:YES];

    是不是瞬間覺得完美了,呵呵,我只能說(shuō)少年,你沒理解兩者之間的區(qū)別。在block中,block是對(duì)變量進(jìn)行捕獲,意思是對(duì)使用到的變量進(jìn)行拷貝操作,注意是拷貝的不是對(duì)象,而是變量自身。拿上面的來(lái)說(shuō),block中只是對(duì)變量wself拷貝了一份,也就是說(shuō),block中也定義了一個(gè)weak對(duì)象,相當(dāng)于,在block的內(nèi)存區(qū)域中,定義了一個(gè)__weak blockWeak對(duì)象,然后執(zhí)行了blockWeak = wself;注意到了沒,這里并沒有引起對(duì)象的持有量的變化,所以沒有問(wèn)題,再看timer的方式,雖然你是將wself傳入了timer的構(gòu)造方法中,我們可以查看NSTimer的

    1
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats1

    定義,其target的說(shuō)明The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated,是要強(qiáng)應(yīng)用這個(gè)變量的 也就是說(shuō),大概是這樣的,__strong strongSelf = wself 強(qiáng)引用了一個(gè)弱應(yīng)用的變量,結(jié)果還是強(qiáng)引用,也就是說(shuō)strongSelf持有了wself所指向的對(duì)象(也即是self所只有的對(duì)象),這和你直接傳self進(jìn)來(lái)是一樣的效果,并不能達(dá)到解除強(qiáng)引用的作用!看來(lái)只能換個(gè)思路了,我直接生成一個(gè)臨時(shí)對(duì)象,讓Timer強(qiáng)用用這個(gè)臨時(shí)對(duì)象,在這個(gè)臨時(shí)對(duì)象中弱引用self,可以了吧。

    4.考慮引入一個(gè)對(duì)象,在這個(gè)對(duì)象中弱引用self,然后將這個(gè)對(duì)象傳遞給timer的構(gòu)建方法 這里可以參考YYWeakProxy建立這個(gè)對(duì)象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    @interface YYWeakProxy : NSProxy
    @property (nonatomic, weak, readonly) id target;
    - (instancetype)initWithTarget:(id)target;
    + (instancetype)proxyWithTarget:(id)target;
    @end
    @implementation YYWeakProxy
    - (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
    }
    + (instancetype)proxyWithTarget:(id)target {
    return [[YYWeakProxy alloc] initWithTarget:target];
    }
    //當(dāng)不能識(shí)別方法時(shí)候,就會(huì)調(diào)用這個(gè)方法,在這個(gè)方法中,我們可以將不能識(shí)別的傳遞給其它對(duì)象處理
    //由于這里對(duì)所有的不能處理的都傳遞給_target了,所以methodSignatureForSelector和forwardInvocation不可能被執(zhí)行的,所以不用再重載了吧
    //其實(shí)還是需要重載methodSignatureForSelector和forwardInvocation的,為什么呢?因?yàn)開target是弱引用的,所以當(dāng)_target可能釋放了,當(dāng)它被釋放了的情況下,那么forwardingTargetForSelector就是返回nil了.然后methodSignatureForSelector和forwardInvocation沒實(shí)現(xiàn)的話,就直接crash了!!!
    //這也是為什么這兩個(gè)方法中隨便寫的!!!
    - (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
    }
    - (void)forwardInvocation:(NSInvocation *)invocation {
    void *null = NULL;
    [invocation setReturnValue:&null];
    }
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    return [NSObject instanceMethodSignatureForSelector:@selector(init)];
    }
    - (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
    }
    - (BOOL)isEqual:(id)object {
    return [_target isEqual:object];
    }
    - (NSUInteger)hash {
    return [_target hash];
    }
    - (Class)superclass {
    return [_target superclass];
    }
    - (Class)class {
    return [_target class];
    }
    - (BOOL)isKindOfClass:(Class)aClass {
    return [_target isKindOfClass:aClass];
    }
    - (BOOL)isMemberOfClass:(Class)aClass {
    return [_target isMemberOfClass:aClass];
    }
    - (BOOL)conformsToProtocol:(Protocol *)aProtocol {
    return [_target conformsToProtocol:aProtocol];
    }
    - (BOOL)isProxy {
    return YES;
    }
    - (NSString *)description {
    return [_target description];
    }
    - (NSString *)debugDescription {
    return [_target debugDescription];
    }
    @end

    使用的時(shí)候,將原來(lái)的替換為:

    1
    2
    3
    4
    5
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1
                                                           target:[YYWeakProxy proxyWithTarget:self ]
                                                         selector:@selector(animationTimerDidFired:)
                                                         userInfo:nil
                                                          repeats:YES];

    5.block方式來(lái)解決循環(huán)引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @interface NSTimer (XXBlocksSupport)
    + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;
    @end
    @implementation NSTimer (XXBlocksSupport)
    + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats
    {
    return [self scheduledTimerWithTimeInterval:interval
                                          target:self
                                        selector:@selector(xx_blockInvoke:)
                                        userInfo:[block copy]
                                         repeats:repeats];
    }
    + (void)xx_blockInvoke:(NSTimer *)timer {
    void (^block)() = timer.userinfo;
    if(block) {
        block();
    }
    }
    @end

    注意:以上NSTimer的target是NSTimer類對(duì)象,類對(duì)象本身是個(gè)單利,此處雖然也是循環(huán)引用,但是由于類對(duì)象不需要回收,所以沒有問(wèn)題。但是這種方式要注意block的間接循環(huán)引用,當(dāng)然了,解決block的間接循環(huán)引用很簡(jiǎn)單,定義一個(gè)weak變量,在block中使用weak變量即可。

      本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評(píng)論

      發(fā)表

      請(qǐng)遵守用戶 評(píng)論公約

      類似文章 更多

      主站蜘蛛池模板: 国产欧美成人XXX视频| 精品午夜久久福利大片| 午夜DY888国产精品影院| 久久这里精品国产99丫E6| 成人免费看片又大又黄| 国产成人精品无码播放| 一本大道中文日本香蕉| 人人妻人人藻人人爽欧美一区| 无码国产偷倩在线播放| 亚洲另类精品无码专区 | 国产精品久久久久久超碰| 少妇被黑人到高潮喷出白浆| 无码国模国产在线观看免费| 国产乱子伦农村叉叉叉| 日本一区二区三区专线| 18无码粉嫩小泬无套在线观看| 啊轻点灬大JI巴太粗太长了在线| 国内精品久久久久影院优| 亚洲精品成人福利网站| 婷婷综合久久中文字幕蜜桃三电影| 女人张开腿无遮无挡视频| 内射毛片内射国产夫妻| 国产AV老师黑色丝袜美腿| 欧美日韩在线视频| 四虎影视永久无码精品| 国产性一交一乱一伦一色一情| 亚洲精品无码你懂的| 久久精品国产99国产精品严洲| 色婷婷综合久久久久中文字幕| 亚洲国产精品久久久天堂麻豆宅男| 97久久天天综合色天天综合色HD| 国产精品电影久久久久电影网 | 国产成人精品中文字幕| 国产色视频一区二区三区| 国产小受被做到哭咬床单GV| 亚洲成AV人无码综合在线| 国产L精品国产亚洲区在线观看| 蜜臀久久精品亚洲一区| 少妇真实被内射视频三四区| 亚洲夂夂婷婷色拍WW47| 国产久免费热视频在线观看|