OC底层原理28-Block上

483 阅读3分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

前言

  1. block面试
  2. 实际业务的开发-问题 循环引用
  3. block底层分析

一.读写锁的补充

1.多读单写

@interface LGRWLock : NSObject
// 读数据
- (id)lg_objectForKey:(NSString *)key;
// 写数据
- (void)lg_setObject:(id)obj forKey:(NSString *)key forTime:(int)time;
@end



#import "LGRWLock.h"

@interface LGRWLock ()
// 定义一个并发队列:
@property (nonatomic, strong) dispatch_queue_t concurrent_queue;
// 用户数据中心, 可能多个线程需要数据访问:
@property (nonatomic, strong) NSMutableDictionary *dataCenterDic;
@end

@implementation LGRWLock

- (id)init{
    self = [super init];
    if (self){
        // 创建一个并发队列:
        self.concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        // 创建数据字典:
        self.dataCenterDic = [NSMutableDictionary dictionary];
    }
    return self;
}

#pragma mark - 读数据 不能异步的原因 因为异步的的objc可能是null直接返回
- (id)lg_objectForKey:(NSString *)key{
    __block id obj;
    // 同步读取指定数据:
    dispatch_sync(self.concurrent_queue, ^{
        obj = [self.dataCenterDic objectForKey:key];
    });
    return obj;
}

#pragma mark - 写数据
- (void)lg_setObject:(id)obj forKey:(NSString *)key forTime:(int)time{
    // 异步栅栏调用设置数据:
   
    dispatch_barrier_async(self.concurrent_queue, ^{
        sleep(time);
        [self.dataCenterDic setObject:obj forKey:key];
        NSLog(@"写情况: %@-%@",self.dataCenterDic[@"name"],[NSThread currentThread]);
    });
}

@end


调用 单写
self.lock = [[LGRWLock alloc] init];
[self.lock lg_setObject:@"kc1" forKey:@"name" forTime:4];
[self.lock lg_setObject:@"kc2" forKey:@"name" forTime:1];
[self.lock lg_setObject:@"kc3" forKey:@"name" forTime:2];
[self.lock lg_setObject:@"kc4" forKey:@"name" forTime:1];

多线程同步读
for (int i = 0; i<5; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"读取情况: %@ - %d -%@",[self.lock lg_objectForKey:@"name"],i,[NSThread currentThread]);
        });
}

二.block的分类

  1. GlobalBlock
  • 位于全局区
  • Block内部不使用外部变量,或者只使用静态变量和全局变量

Xnip2021-08-24_21-32-41.jpg 2. MallocBlock

  • 位于堆区
  • Block内部使用变量或者OC属性,并且赋值给强引用或者Copy修饰的变量

Xnip2021-08-24_21-33-16.jpg 3. StackBlock

  • 位于栈区
  • MallocBlock一样,可以在内部使用局部变量或者OC属性.但是不能赋值给强引用或者Copy修饰的变量

Xnip2021-08-24_21-33-47.jpg

#pragma mark - block 捕获外部变量-对外部变量的引用计数处理
- (void)blockDemo2{
    
    NSObject *objc = [NSObject new];
    
    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)(objc))); // 1
    // block 底层源码
    // 捕获 + 1
    // 堆区block
    // 栈 - 内存 -> 堆  + 1
    void(^strongBlock)(void) = ^{ // 1 - block -> 持有 objc 捕获 + 1 ->从栈拷贝到堆上+1 = 3
        NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
    };
    strongBlock();
    
    void(^__weak weakBlock)(void) = ^{ // + 1
        NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
    };
    weakBlock();
    
    void(^mallocBlock)(void) = [weakBlock copy];//+1
    mallocBlock();
    
}

打印结果
2021-08-24 21:47:01.076060+0800 001---Block深入浅出[73229:1215462] 1
2021-08-24 21:47:01.076239+0800 001---Block深入浅出[73229:1215462] ---3
2021-08-24 21:47:01.076367+0800 001---Block深入浅出[73229:1215462] ---4
2021-08-24 21:47:01.076467+0800 001---Block深入浅出[73229:1215462] ---5
  • objc创建 1
  • block 捕获+1 从栈拷贝一份到堆+1 所以是3
  • block 捕获+1 所以打印4
  • block copy+1 所以打印5
- (void)blockDemo1{
    int a = 0;
    //栈block
    void(^ __weak weakBlock)(void) = ^{
        NSLog(@"-----%d", a);
    };
    struct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;
  
    // 变为堆block 深拷贝一份 调用无影响  如果不copy的话 操作同一个地址 调用崩溃
    id __strong strongBlock = [weakBlock copy];
    blc->invoke = nil;
    void(^strongBlock1)(void) = strongBlock;
    strongBlock1();
    // 预警什么 ???
}
- (void)blockDemo3{
    
    NSObject *a = [NSObject alloc];
    void(^__weak weakBlock)(void) = nil;
    {   
        //堆block作用域在{}里面 出了就销毁了 所以调用崩溃
        void(^strongBlock)(void) = ^{
            NSLog(@"---%@", a);
        };
        weakBlock = strongBlock;
        NSLog(@"1 - %@ - %@",weakBlock,strongBlock);
    }
    weakBlock();
}
- (void)blockDemo3{
    
    NSObject *a = [NSObject alloc];
    void(^__weak weakBlock)(void) = nil;
    {   
        //栈block作用域 在整个函数 调用正常 栈block不能持有赋值 不能调用 堆block可以赋值持有可以调用
        void(^__weak strongBlock)(void) = ^{
            NSLog(@"---%@", a);
        };
        weakBlock = strongBlock;
        NSLog(@"1 - %@ - %@",weakBlock,strongBlock);
    }
    weakBlock();
}

三.block循环引用

self.xwblock = ^(){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",self.name);
        });
};
代码循环引用 self持有xwblock xwblock捕获self.name互相持有
__weak typeof(self) weakSelf = self;
self.xwblock = ^(){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",weakSelf.name);
        });
};
如果weakSelf提前释放 这里打印的就是空
//weak-strong-dance
 __weak typeof(self) weakSelf = self;
    self.xwblock = ^(){
        __strong typeof(weakSelf) strongSelf = weakSelf;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",strongSelf.name);
        });
    };
  正常打印
// self -> block -> vc -> self
    __block ViewController *vc = self;
    self.block = ^(void){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",vc.name);
            vc = nil;
        });
    };
//block传参数
self.block = ^(ViewController *vc){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",vc.name);
        });
    };
    self.block(self);

四.block循环引用的面试题

static ViewController * staticSelf_;


- (void)blockWeak_static {
    // 是同一片内存空间
    __weak typeof(self) weakSelf = self;
    staticSelf_ = weakSelf;
    // staticSelf_ -> weakSelf -> self
}

全局静态变量持有 没有释放 

- (void)block_weak_strong {
    __weak typeof(self) weakSelf = self;
    self.doWork = ^{
        __strong typeof(self) strongSelf = weakSelf;
        //捕获strongSelf 导致无法释放
        weakSelf.doStudent = ^{
            NSLog(@"%@", strongSelf);
            //strongSelf = nil;
        };
        weakSelf.doStudent();
    };
   self.doWork();
}