iOS底层知识学习整理:
1. OC语法
1.1 isa指针:
实例的本质,是一个结构体,里面包含了一个 Class类型的变量isa
typedef struct objc_class *Class;
struct objc_object {
Class isa;
};
typedef struct objc_object *id;
1.2 class object(类对象) metaclass(元类)
实例的isa指针指向类对象,类对象的isa指针指向元类对象
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
// objc_class继承于objc_object,因此
// objc_class中也有isa结构体
struct objc_class : objc_object {
Class ISA; //8字节
Class superclass;//8字节
// 缓存的是指针和vtable,目的是加速方法的调用 cache占16字节
cache_t cache; // formerly cache pointer and vtable
// class_data_bits_t 相当于是class_rw_t 指针加上rr/alloc标志
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...
};
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
...
};
struct cache_t {
struct bucket_t *_buckets;//8字节
mask_t _mask;//4字节
mask_t _occupied;//4字节
};
成员变量的具体值,存放在instance对象
对象方法、属性、成员变量、协议信息,存放在class对象中
类方法,存放在meta-class对象中
instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基类的meta-class
class的superclass指向父类的class
如果没有父类,superclass指针为nil
meta-class的superclass指向父类的meta-class
基类的meta-class的superclass指向基类的class
instance调用对象方法的轨迹
isa找到class,方法不存在,就通过superclass找父类
1.3 KVO的实现:
利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
NSKVONotifying_CHSPerson 中setAge(伪代码):
- (void)setAge:(NSInteger)age {
_NSSetIntValueAndNotify();
}
void _NSSetIntValueAndNotify () {
[self willChangeValueForKey:@"age"];
//父类原来的setter
[self didChangeValueForKey:@"age"]; //内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法
}
1.4 KVC的实现:可以通过一个key来访问某个属性
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
1.5 + (void)load方法 && + (void)initialize方法
+ (void)load方法是根据方法地址直接调用
+ (void)load方法会在runtime加载类、分类时调用,每个类、分类的+ (void)load在程序运行过程中只调用一次
调用顺序:
先调用类的+load
按照编译先后顺序调用(先编译,先调用)
调用子类的+load之前会先调用父类的+load
再调用分类的+load
按照编译先后顺序调用(先编译,先调用)
+ (void)initialize方法是通过objc_msgSend进行调用的
+ (void)initialize方法会在类第一次接收到消息时调用
调用顺序:
先调用父类的+ (void)initialize,再调用子类的+ (void)initialize
先初始化父类,再初始化子类,每个类只会初始化1次
1.6 Category
1.6.1 通过Runtime加载某个类的所有Category数据
1.6.2 把所有Category的方法、属性、协议数据,合并到一个大数组中,后面参与编译的Category数据,会在数组的前面
1.6.3 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
2.内存管理:
2.1 copy关键字
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name copy];
}
}
2.2 retain关键字
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
2.3 AutoReleasePool 原理
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()
1.第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池
2.第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池
手动autoreleasePool:@autoreleasepool {}方式手动创建autoreleasepool对象,出了autoreleasepool的大括号就释放了
1.AutoreleasePool是由若干个AutoreleasePoolPage以双向链表的形式组合而成【parent指针和child指针】
2.AutoreleasePoolPage每个对象会开辟4096字节内存,除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址
3.上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置。
4.一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入
5.每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
3.Runtime:
3.1 给类别添加关联对象:
类别中可以写属性,只会生成属性的声明,不会有实现
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int weight;
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
// 隐式参数
// _cmd == @selector(name)
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWeight:(int)weight
{
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight
{
// _cmd == @selector(weight)
return [objc_getAssociatedObject(self, _cmd) intValue];
}
static void *CHSKey = &CHSKey;
objc_setAssociatedObject(obj, CHSKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, CHSKey)
3.2 消息机制:
OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
objc_msgSend底层有3大阶段
消息发送(当前类、父类中查找)、动态方法解析、消息转发
3.3 消息转发流程:
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 第一个参数是object_getClass(self)
class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
return YES;
}
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 动态添加test方法的实现
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)other
{
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 获取其他方法
Method method = class_getInstanceMethod(self, @selector(other));
// 动态添加test方法的实现
class_addMethod(self, sel,
method_getImplementation(method),
method_getTypeEncoding(method));
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
// objc_msgSend([[CHSCat alloc] init], @selector(test))
// [[[CHSCat alloc] init] test]
if (aSelector == @selector(test)) return [[CHSCat alloc] init];
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test:)) {
// return [NSMethodSignature signatureWithObjCTypes:"v20@0:8i16"];
return [NSMethodSignature signatureWithObjCTypes:"i@:I"];
// return [[[CHSCat alloc] init] methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// 参数顺序:receiver、selector、other arguments
// anInvocation.target == [[CHSCat alloc] init]
// anInvocation.selector == test:
// anInvocation的参数:15
// [[[CHSCat alloc] init] test:15]
[anInvocation invokeWithTarget:[[CHSCat alloc] init]];
int ret;
[anInvocation getReturnValue:&ret];
NSLog(@"%d", ret);
}
3.4 方法交换:
获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
#import "UIImage+change_m.h"
#import
@implementation UIImage(change_m)
+ (void)load {
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(st_imageNamed:));
method_exchangeImplementations(m1, m2);
}
+ (UIImage *)st_imageNamed:(NSString *)name {
return [UIImage st_imageNamed:[NSString stringWithFormat:@"%@",name]];
}
@end
3.5 runtime的具体作用:
3.5.1 利用关联对象(AssociatedObject)给分类添加属性
3.5.2 遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
3.5.3 交换方法实现(交换系统的方法)
3.5.4 利用消息转发机制解决方法找不到的异常问题
4.Runloop:
4.1. RunLoop的基本作用:
1.保持程序的持续运行
2.处理App中的各种事件(比如触摸事件、定时器事件等)
3.节省CPU资源,提高程序性能:该做事时做事,该休息时休息
4.2. RunLoop与线程的关系:
1.每条线程都有唯一的一个与之对应的RunLoop对象
2.RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
3.线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
4.RunLoop会在线程结束时销毁
5.主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
4.3. RunLoop的组成:
1.CFRunLoopModeRef代表RunLoop的运行模式
2.一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
3. RunLoop启动时只能选择其中一个Mode,作为currentMode
4.如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
4.4. CFRunLoopModeRef的种类:
1. kCFRunLoopDefaultMode App的默认Mode,通常主线程是在这个Mode下运行
2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
3. UIInitializationRunLoopMode 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
4. GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
5. kCFRunLoopCommonModes 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
4.5.Source:
1.Source0: 触摸事件处理 || performSelector:onThread:
2.Source1: 基于Port的线程间通信 || 系统事件捕捉
4.6. CFRunLoopObserverRef的状态:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),即将进入runloop
kCFRunLoopBeforeTimers = (1UL << 1),即将处理timer事件
kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件
kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠
kCFRunLoopAfterWaiting = (1UL << 6),被唤醒的状态
kCFRunLoopExit = (1UL << 7),runloop退出
kCFRunLoopAllActivities = 0x0FFFFFFFU 所有的状态
};
4.7. RunLoop的运行逻辑:
01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop
5.block:
5.1 block的本质:
block本质上也是一个OC对象,它内部也有个isa指针,封装了函数调用以及函数调用环境的OC对象
5.2 block的变量捕获:
//全局变量:
int age_ = 10;
int main(int argc, char * argv[]) {
@autoreleasepool {
//static局部变量:
static int height_ = 10;
//auto局部变量:
auto int weight_ = 10;
void (^block)(void) = ^{
CHSLOG(@"age is %d, height is %d,weight is %d", age_, height_,weight_);
};
age_ = 20;
height_ = 20;
weight_ = 20;
block();
}
return 0;
}
// age is 20, height is 20,weight is 10
5.3 block的类型:
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
1.block作为函数返回值时
2.将block赋值给__strong指针时
3.block作为Cocoa API中方法名含有usingBlock的方法参数时
4.block作为GCD API的方法参数时
5.4 block访问对象类型的auto变量:
若block是在栈上:不会对auto局部变量产生强引用
若block被copy到堆上:
1.1 会调用block内部的copy函数
1.2 copy函数内部会调用_Block_object_assign函数
1.3 _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
若block从堆上移除:
1.1 会调用block内部的dispose函数
1.2 dispose函数内部会调用_Block_object_dispose函数
1.3 _Block_object_dispose函数会自动释放引用的auto变量(release)
5.5 __block修饰符
__block可以用于解决block内部无法修改auto变量值的问题
[因为auto局部变量在block中是值传递捕获,auto局部变量是在栈中,程序执行完该作用域,就会被销毁,所以需要通过__block把auto局部变量放在堆中,当auto变量加上__block修饰符时,会把该变量封装成一个block结构体,在这个结构体中有个forwarding指针,指向自身,并且这个结构体中包含有auto变量]
__block不能修饰全局变量、静态变量(static)
5.6 block访问对象类型的__block局部变量:
若__block是在栈上: 不会对指向的对象产生强引用
若__block被copy到堆上:
1.1 会调用__block内部的copy函数
1.2 copy函数内部会调用_Block_object_assign函数
1.3 _Block_object_assign函数会根据__block变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。(注意:这里仅限于ARC时会retain,MRC时不会retain)
若block从堆上移除:
1.1 会调用block内部的dispose函数
1.2 dispose函数内部会调用_Block_object_dispose函数
1.3 _Block_object_dispose函数会自动释放引用的__block变量(release)
5.7 block循环引用
ARC:
1.1 使用__weak修饰符(不会产生强引用,指向的对象销毁时,会自动让指针置为nil)
1.2 使用__unsafe_unretained修饰符(不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)
1.3 使用__block修饰符(必须调用block,并在执行完block中的内容后,把修饰的该对象置为nil)
MRC:
1.1 使用__unsafe_unretained修饰符(不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)
1.2 使用__block修饰符(因为在MRC中,__block不会对持有对象进行强引用retain)
6. 多线程
6.1 iOS中的线程同步方案
"自旋锁":等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源,有OSSpinLock
"互斥锁":同一时刻只能有一个线程获得互斥锁,其余线程处于挂起状态
锁的性能从高到低排序
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
6.1.1 OSSpinLock (自旋锁)
#import
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&oslock);
NSLog(@"线程1");
OSSpinLockUnlock(&oslock);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&oslock);
NSLog(@"线程2");
OSSpinLockUnlock(&oslock);
});
6.1.2 os_unfair_lock (互斥锁)
//初始化:
os_unfair_lock unsafeMutex = OS_UNFAIR_LOCK_INIT;
//加锁:
os_unfair_lock_lock(&unsafeMutex)
//尝试加锁:
os_unfair_lock_trylock(&unsafeMutex)
//解锁:
os_unfair_lock_unlock(&unsafeMutex)
6.1.3 pthread_mutex (互斥锁)
#import
@property (assign, nonatomic) pthread_mutex_t mutex;
- (void)__initMutex:(pthread_mutex_t *)mutex
{
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
}
[self __initMutex:&_mutex];
//加锁:
pthread_mutex_lock(&_mutex);
//解锁:
pthread_mutex_unlock(&_mutex);
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
}
6.1.4 pthread_mutex – 递归锁
#import
@property (assign, nonatomic) pthread_mutex_t mutex;
- (void)__initMutex:(pthread_mutex_t *)mutex
{
// 递归锁:允许同一个线程对一把锁进行重复加锁
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
}
[self __initMutex:&_mutex];
//加锁:
pthread_mutex_lock(&_mutex);
//解锁:
pthread_mutex_unlock(&_mutex);
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
}
6.1.5 pthread_mutex – 条件锁
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
- (void)__initMutex:(pthread_mutex_t *)mutex
{
// 递归锁:允许同一个线程对一把锁进行重复加锁
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
// 初始化条件
pthread_cond_init(&_cond, NULL);
}
[self __initMutex:&_mutex];
//加锁:
pthread_mutex_lock(&_mutex);
// 等待:
pthread_cond_wait(&_cond, &_mutex);
//解锁:
pthread_mutex_unlock(&_mutex);
// 信号:
pthread_cond_signal(&_cond);
// 广播:
pthread_cond_broadcast(&_cond);
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
6.1.6 NSLock和NSRecursiveLock是对mutex普通锁的封装(互斥锁)
NSLock *semLock = [[NSLock alloc] init];
[semLock lock];
[semLock unlock];
6.1.7 NSCondition是对mutex和cond的封装(互斥锁)
NSCondition *condition = [[NSCondition alloc] init];
// 加锁:
[condition lock];
// 等待:
[condition wait];
// 解锁:
[self.condition unlock];
// 信号:
[self.condition signal];
// 广播:
[self.condition broadcast];
6.1.8 NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值(互斥锁)
@property (strong, nonatomic) NSConditionLock *conditionLock;
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
- (void)start
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}
- (void)__one
{
[self.conditionLock lock];
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
- (void)__three
{
[self.conditionLock lockWhenCondition:3];
NSLog(@"__three");
[self.conditionLock unlock];
}
6.1.9 dispatch_queue(串行队列)
@property (strong, nonatomic) dispatch_queue_t chsQueue;
self. chsQueue = dispatch_queue_create("chsQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(self. chsQueue, ^{
[super __drawMoney];
});
6.1.10 dispatch_semaphore(互斥锁)
信号量的初始值,可以用来控制线程并发访问的最大数量
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
@property (strong, nonatomic) dispatch_semaphore_t semaphore;
self.semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(self.semaphore);
6.1.11 @synchronized是对mutex递归锁的封装(互斥锁)
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
@synchronized([self class]) {
}
6.2 atomic
用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁
7. 解决NSTimer的循环引用:
7.1 CADisplayLink && NSTimer
CADisplayLink 是一个定时器对象可以让你的应用以与显示器的刷新界面相同的频率进行绘图
@interface CHSProxy : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation CHSProxy
+ (instancetype)proxyWithTarget:(id)target {
CHSProxy *proxy = [[CHSProxy alloc] init];
proxy.target = target;
return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
return self.target;
}
@end
@property (strong, nonatomic) CADisplayLink *cadLink;
@property (strong, nonatomic) NSTimer *timer;
// 保证调用频率和屏幕的刷帧频率一致,60FPS
self.cadLink = [CADisplayLink displayLinkWithTarget:[CHSProxy proxyWithTarget:self] selector:@selector(fireFunc)];
[self.cadLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[MJProxy proxyWithTarget:self] selector:@selector(fireFunc) userInfo:nil repeats:YES];
- (void)fireFunc
{
NSLog(@"%s", _cmd);
}
7.2 NSTimer NSProxy的使用
@interface CHSProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation CHSProxy
+ (instancetype)proxyWithTarget:(id)target {
// NSProxy对象不需要调用init,因为它本来就没有init方法
CHSProxy *proxy = [CHSProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
@end
7.2 NSTimer的封装
@interface CHSTimer : NSObject
+ (NSString *)execTask:(void(^)(void))task
start:(NSTimeInterval)start
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
async:(BOOL)async;
+ (NSString *)execTask:(id)target
selector:(SEL)selector
start:(NSTimeInterval)start
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
async:(BOOL)async;
+ (void)cancelTask:(NSString *)name;
@end
@implementation CHSTimer
static NSMutableDictionary *timers_;
dispatch_semaphore_t semaphore_;
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
timers_ = [NSMutableDictionary dictionary];
semaphore_ = dispatch_semaphore_create(1);
});
}
+ (NSString *)execTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
if (!task || start < 0 || (interval <= 0 && repeats)) return nil;
// 队列
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, start * NSEC_PER_SEC),
interval * NSEC_PER_SEC, 0);
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
// 定时器的唯一标识
NSString *name = [NSString stringWithFormat:@"%zd", timers_.count];
// 存放到字典中
timers_[name] = timer;
dispatch_semaphore_signal(semaphore_);
// 设置回调
dispatch_source_set_event_handler(timer, ^{
task();
if (!repeats) { // 不重复的任务
[self cancelTask:name];
}
});
// 启动定时器
dispatch_resume(timer);
return name;
}
+ (NSString *)execTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
if (!target || !selector) return nil;
return [self execTask:^{
if ([target respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:selector];
#pragma clang diagnostic pop
}
} start:start interval:interval repeats:repeats async:async];
}
+ (void)cancelTask:(NSString *)name
{
if (name.length == 0) return;
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = timers_[name];
if (timer) {
dispatch_source_cancel(timer);
[timers_ removeObjectForKey:name];
}
dispatch_semaphore_signal(semaphore_);
}
@end
@property (copy, nonatomic) NSString *task;
// 接口设计
self.task = [CHSTimer execTask:self
selector:@selector(doTask)
start:2.0
interval:1.0
repeats:YES
async:NO];
- (void)doTask
{
NSLog(@"doTask - %@", [NSThread currentThread]);
}
[CHSTimer cancelTask:self.task];