iOS底层知识学习整理-MJ老师逆向班

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对象中
WechatIMG21.png
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找父类

isa指针.jpeg

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:方法
}
WechatIMG22.png

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; 
setValue:forKey:的原理.png

valueForKey:的原理.png

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 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
Category的底层结构.png

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;
}
autoReleasePool_push.png
autoReleasePool_pop.png

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 消息转发流程:

objc_msgSend消息发送.png
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];
}
objc_msgSend动态方法解析.png
+ (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);
}
objc_msgSend消息转发.png

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 <objc/runtime.h>
@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会立马退出
RunLoop的组成.png
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
block的变量捕获.png

5.3 block的类型:

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
1.block作为函数返回值时
2.将block赋值给__strong指针时
3.block作为Cocoa API中方法名含有usingBlock的方法参数时
4.block作为GCD API的方法参数时
block的类型.png

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循环引用

block循环引用.png
    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 <libkern/OSAtomic.h>
__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 <pthread.h>
@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 <pthread.h>
@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];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容