iOS 开发 Objective-C

iOS 底层 day23 多线程 NSTimer CADispl

2020-09-22  本文已影响0人  望穿秋水小作坊

一、NSTimer

1.基本特点
2. 用法一 block
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) NSTimer *timer;
@property(nonatomic, assign) int age;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.age = 10;
    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"age is age %d", weakSelf.age);
    }];
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.timer invalidate];
    self.timer = nil;
}
@end
3. 用法二 target,思考如下代码有问题吗?
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
}
- (void)task {
    NSLog(@"do tesk");
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.timer invalidate];
    self.timer = nil;
}
@end
4. 用法二 target,可能有人会这么想,如下代码能解决循环引用吗?
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(task) userInfo:nil repeats:YES];
}
- (void)task {
    NSLog(@"do tesk");
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.timer invalidate];
    self.timer = nil;
}
@end
5. 用法二 target,viewWillDisappear手动断环思路
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
}
- (void)task {
    NSLog(@"do tesk");
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    NSLog(@"%s", __func__);
    [self.timer invalidate];
    self.timer = nil;
}

- (void)dealloc {
    NSLog(@"%s", __func__);
}
@end
6. 用法二 target,代理断环(代理继承自 NSObject)
代理断环原理图
#import "YYObjectProxy.h"
@interface YYObjectProxy ()
@property(nonatomic, weak)id target;
@end
@implementation YYObjectProxy
+ (instancetype)proxyWithTarget:(id)target {
    YYObjectProxy *proxy =  [[YYObjectProxy alloc] init];
    proxy.target = target;
    return proxy;
}
  // 消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return self.target;
}
- (void)dealloc {
    NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "YYObjectProxy.h"
@interface ViewController ()
@property(nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    YYObjectProxy *proxy = [YYObjectProxy proxyWithTarget:self];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:proxy selector:@selector(task) userInfo:nil repeats:YES];
}
- (void)task {
    NSLog(@"do tesk");
}

- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.timer invalidate];
    self.timer = nil;
}
@end
7. 用法二 target,代理断环(代理继承自 NSProxy,也是暂时比较完美的解决方案)

我们使用继承自 NSObject 的代码固然能解决循环引用问题,但是上面代码还有更好的继承方案,那就是继承自 NSProxy。

// YYProxy.m
#import "YYProxy.h"

@interface YYProxy ()
@property(nonatomic, weak) id target;
@end

@implementation YYProxy
+(instancetype)proxyWithTarget:(id)target {
    // NSProxy对象不需要调用init,因为它本来就没有init方法
    YYProxy *proxy = [YYProxy alloc];
    proxy.target = target;
    return proxy;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return  [self.target methodSignatureForSelector:sel];
}

-(void)dealloc {
    NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "YYProxy.h"
@interface ViewController ()
@property(nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    YYProxy *proxy = [YYProxy proxyWithTarget:self];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:proxy selector:@selector(task) userInfo:nil repeats:YES];
}
- (void)task {
    NSLog(@"do tesk");
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.timer invalidate];
    self.timer = nil;
}
@end
8. 结合上面实例7和实例6的代码,下面代码输出什么?
#import "ViewController.h"
#import "YYProxy.h"
#import "YYObjectProxy.h"
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    YYProxy *proxy1 = [YYProxy proxyWithTarget:self];
    YYObjectProxy *proxy2 = [YYObjectProxy proxyWithTarget:self];
    
    NSLog(@"%d %d",
          [proxy1 isKindOfClass:[ViewController class]],
          [proxy2 isKindOfClass:[ViewController class]]
          );
}

二、CDDisplayLink

1.基本特点
2.基本用法
#import "YYProxy.h"
@interface YYProxy ()
@property(nonatomic, weak) id target;
@end

@implementation YYProxy
+(instancetype)proxyWithTarget:(id)target {
    // NSProxy对象不需要调用init,因为它本来就没有init方法
    YYProxy *proxy = [YYProxy alloc];
    proxy.target = target;
    return proxy;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return  [self.target methodSignatureForSelector:sel];
}

-(void)dealloc {
    NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "YYProxy.h"
#import "YYObjectProxy.h"
@interface ViewController ()
@property(nonatomic, strong) CADisplayLink *displayLink;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    YYProxy *proxy = [YYProxy proxyWithTarget:self];
    self.displayLink = [CADisplayLink displayLinkWithTarget:proxy selector:@selector(task)];
    self.displayLink.preferredFramesPerSecond = 1;
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)task {
    NSLog(@"do tesk");
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.displayLink invalidate];
    self.displayLink = nil;
}
@end

三、GCD定时器

1. 基本特点
2. 主要方法
主要方法介绍
3. 代码演示(已封装成 CGD 定时器工具类)
#import "YYTimer.h"

@implementation YYTimer

NSMutableDictionary *timerDictionary_;
dispatch_semaphore_t timerSemaphore_;

+(void)initialize{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        timerDictionary_ = [NSMutableDictionary dictionary];
        timerSemaphore_ = dispatch_semaphore_create(1);
    });
}
+ (NSString*)executeTask:(taskBlock)task start:(uint64_t)startInSeconds interval:(uint64_t)intervalInSeconds repeat:(BOOL)repeat async:(BOOL)async {
    // 创建timer
    dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, startInSeconds * NSEC_PER_SEC), intervalInSeconds * NSEC_PER_SEC, 0);
    
    dispatch_semaphore_wait(timerSemaphore_, DISPATCH_TIME_FOREVER);
    // 创建唯一标识
    static int counter = 0;
    NSString *timerName = [NSString stringWithFormat:@"%d",counter++];
    // 放入全局字典中
    [timerDictionary_ setObject:timer forKey:timerName];
    dispatch_semaphore_signal(timerSemaphore_);
    dispatch_source_set_event_handler(timer, ^{
        task();
        if (!repeat) {
            [YYTimer cancelTimer:timerName];
        }
    });
    dispatch_resume(timer);
    return timerName;
}

+ (void)cancelTimer:(NSString *)timerName {
    if (timerName.length == 0) return;
    dispatch_semaphore_wait(timerSemaphore_, DISPATCH_TIME_FOREVER);
    dispatch_source_t timer = [timerDictionary_ objectForKey:timerName];
    if (timer) {
        [timerDictionary_ removeObjectForKey:timerName];

    }
    dispatch_semaphore_signal(timerSemaphore_);
}
@end

#import <Foundation/Foundation.h>

typedef void(^taskBlock)(void);

@interface YYTimer : NSObject

+ (NSString*)executeTask:(taskBlock)task
                   start:(uint64_t)startInSeconds
                interval:(uint64_t)intervalInSeconds
                  repeat:(BOOL)repeat
                   async:(BOOL)async;

+ (void)cancelTimer:(NSString *)timerName;
@end
#import "ViewController.h"
#import "YYTimer.h"
@interface ViewController ()
@property(nonatomic, strong) NSString *timerName;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.timerName = [YYTimer executeTask:^{
        NSLog(@"112%@",[NSThread currentThread]);
    } start:2.0 interval:1.0 repeat:YES async:YES];
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [YYTimer cancelTimer:self.timerName];
}
@end
上一篇下一篇

猜你喜欢

热点阅读