“这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战”
前言
block面试- 实际业务的开发-问题 循环引用
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的分类
GlobalBlock
- 位于全局区
- 在
Block内部不使用外部变量,或者只使用静态变量和全局变量
2.
MallocBlock
- 位于堆区
- 在
Block内部使用变量或者OC属性,并且赋值给强引用或者Copy修饰的变量
3.
StackBlock
- 位于栈区
- 与
MallocBlock一样,可以在内部使用局部变量或者OC属性.但是不能赋值给强引用或者Copy修饰的变量
#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- 栈
blockcopy+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();
}