1.block本质是封装了函数调用以及函数调用环境的oc对象。自己要会根据源码分析出这个结论记忆会更深刻。
函数调用:函数地址
函数调用环境:block 访问外部的变量
OC对象:有isa指针
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
};
int age = 20;
void (^block)(int, int) = ^(int a , int b){
NSLog(@"this is a block! -- %d", age);
NSLog(@"this is a block!");
NSLog(@"this is a block!");
NSLog(@"this is a block!");
};
struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
block(10, 10);
2.请分析为什么打印的值是10?
auto int age = 10; //定义出来的局部变量都是auto,可以省略不写,意思是离开所在的大括号会自动销毁
block = ^{
// age的值捕获进来(capture)
NSLog(@"age is %d", age);
};
age = 20;
block();
因为block具备捕获的能力,在block内已经把10这个值传递给block内部的成员变量了,所以打印10,而这个时候去修改age的值其实是修改的block外部的age。
捕获:block内部会新增一个成员变量正好是外部的变量
3.为什么自动变量是值传递不是指针传递?
因为自动变量在函数执行完后会销毁,销毁后再去访问就是垃圾数据了.
什么是自动把变量?
局部变量分2种:auto、static
auto int age = 10; //定义出来的局部变量都是auto,auto只能修饰局部变量,可以省略不写,意思是离开所在的大括号会自动销毁
void (^block)(void);
void test()
{
auto int age = 10;
static int height = 10;
block = ^{
// age的值捕获进来(capture)
NSLog(@"age is %d, height is %d", age, height);
};
age = 20;
height = 20;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
block(); //age is 10, height is 20
}
}
4.全局变量为什么不用捕获可以直接访问?
因为谁都可以随时访问就没有必要访问了。
为什么局部变量需要捕获?test已经结束了,block又去访问,要想访问到需要先把值存在block即捕获,即跨函数访问的原因。
5.block变量捕获的种类?3种
变量类型 捕获到block内部 访问方式
局部 auto 基本数据类型 ✔️ 值传递
auto 对象类型 ✔️ 连同其所有权修饰符的指针传递
变量 static ✔️ 指针传递
全局 变量 × 直接访问
究竟block会不会捕获只要搞清楚是局部变量还是全局变量就行了
6.下边例子会不会捕获?
会。只要搞清楚是局部变量还是全局变量就行了,因为OC函数默认为传递2个参数(MJPerson * self, SEL _cmd)函数的调用者和方法名。
Person.m
- (void)test {
void(^block)(void) = ^ {
nslog(@"%@",self);
nslog(@"%@",_name);//self->name //直接将self捕获,不是单独不会_name,看_name来源,同理[self name]也一样
}
}
7.block类型有哪几种?
block类型 环境
__NSGlobalBlock__ 没有访问auto变量
__NSStackBlock__ 访问了auto变量
__NSMallocBlock__ __NSMallocBlock__调用了copy
每种block调用copy后的结果:
block的类 副本源的配置存储域 复制结果
__NSConcreteGlobalBlock__ 栈 堆
__NSConcreteStackBlock__ 数据段 什么也不做
__NSConcreteMallocBlock__ 堆 引用计数增加
8.ARC下block自动copy的情况有哪些?
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
(1)block作为函数返回值时
typedef void (^MJBlock)(void);
MJBlock myblock()
{
int age = 10;
return ^{
NSLog(@"---------%d",age);
};
}
main() {
MJBlock block = myblock();
block();
NSLog(@"%@", [block class]); //__NSMallocBlock__
}
(2)将block赋值给__strong指针时
int age = 10;
MJBlock block = ^{
NSLog(@"---------%d", age);
};
NSLog(@"%@", [block class]); //__NSMallocBlock__
(3)block作为Cocoa API中方法名含有usingBlock的方法参数时
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
(4)block作为GCD API的方法参数时
dispatch_once(&onceToken, ^{
});
9.栈和堆访问对象类型的auto变量时都发生了什么?
栈block不会对auto变量产生强引用;
block被拷贝到堆上,block会调用copy函数
10.打印什么?怎么解释?-什么时候被释放的问题?
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
MJPerson *p = [[MJPerson alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1-------%@", p);
});
NSLog(@"touchesBegan:withEvent:");
}
/*
touchesBegan:withEvent:
1-------<MJPerson: 0x6000008f04b0>
MJPerson - dealloc
*/
block作为GCD的参数会发生copy,将栈上的block复制到堆上;
MJPerson *p是对象类型的强引用,在堆block内部会强引用p,在1s后才会释放block,在释放block前会先释放block强引用的p
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
MJPerson *p = [[MJPerson alloc] init];
__weak MJPerson *weakP = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2-------%@", weakP);
});
NSLog(@"touchesBegan:withEvent:");
}
/*
touchesBegan:withEvent:
MJPerson - dealloc
2-------(null)
*/
block作为GCD的参数会发生copy,将栈上的block复制到堆上;
在大括号结束时p 会被释放掉;
MJPerson *p是对象类型的弱引用,在堆block内部会弱引用p,在1s后才会释放block,而此时weakP已被释放所以null。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
MJPerson *p = [[MJPerson alloc] init];
__weak MJPerson *weakP = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1-------%@", p);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2-------%@", weakP);
});
});
NSLog(@"touchesBegan:withEvent:");
}
2021-04-11 11:36:34.831422+0800 Interview03-测试[17362:9913914] touchesBegan:withEvent:
2021-04-11 11:36:34.831549+0800 Interview03-测试[17362:9913914] MJPerson - dealloc
2021-04-11 11:36:35.929587+0800 Interview03-测试[17362:9913914] 2-------(null)
2个block整体看待即可,2s后被释放。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
MJPerson *p = [[MJPerson alloc] init];
__weak MJPerson *weakP = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1-------%@", weakP);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2-------%@", p);
});
});
NSLog(@"touchesBegan:withEvent:");
}
2021-04-11 13:41:36.149167+0800 Interview03-测试[17689:9981812] touchesBegan:withEvent:
2021-04-11 13:41:37.149242+0800 Interview03-测试[17689:9981812] 1-------<MJPerson: 0x6000028b4230>
2021-04-11 13:41:39.342495+0800 Interview03-测试[17689:9981812] 2-------<MJPerson: 0x6000028b4230>
2021-04-11 13:41:39.342795+0800 Interview03-测试[17689:9981812] MJPerson - dealloc
2个block整体看待即可,第2个block强引用p,3s后被释放。
总结:看强引用什么时候被释放。就是几秒后被销毁。
11.MRC和ARC下block写法是怎么样的?
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
ARC下block属性的建议写法:以下2中写法等价
@property (strong, nonatomic) void (^block)(void); ARC下也会拷贝到堆上
@property (copy, nonatomic) void (^block)(void);
12.有哪些方式可以解决在block内部修改外部变量的值?改为__block,static修饰,全局变量
为什么不用static修饰,全局变量呢?因为static、全局变量会一直在内存中,只是希望在内存中暂时存一下。
13.理解__block变量
__block可以用于解决block内部无法修改auto变量值的问题
__block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象
__block int age = 10;
^{
nslog(@"%d",age);
}();
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
}
14.block访问__block基本数据类型、对象类型变量、__block对象类型的区别?
强引用;
由其修饰符决定;
block对__block变量结构体强引用,结构体对对象类型变量由其修饰符决定。
__block int age = 10;
NSObject *object = [[NSObject alloc] init]; //这种修饰符默认strong
__weak NSObject *weakObject = object; //这种修饰符默认weak
MJBlock block = ^{
NSLog(@"%d", age); //block强引用object
NSLog(@"%p", weakObject); //block弱引用weakObject
};
block();
block对__block变量结构体强引用,ARC下结构体对对象类型变量由其修饰符决定,
MRC下结构体对对象类型变量不会强引用。
ARC:
(1)strong情况:
MJBlock block;
{
__block MJPerson *person = [[MJPerson alloc] init];
block = ^{
NSLog(@"%p", person);// block内部person结构体的person指针默认是 strong
};
}
block();
//0x1060575b0
(2)weak情况:
MJBlock block;
{
MJPerson *person = [[MJPerson alloc] init];
__block __weak MJPerson *weakPerson = person;
block = ^{
NSLog(@"%p", weakPerson);//block内部person结构体的person指针默认是 weak
};
}
block();
2021-04-11 23:14:48.230528+0800 Interview01-__block[19517:10315770] -[MJPerson dealloc]
2021-04-11 23:14:48.231074+0800 Interview01-__block[19517:10315770] 0x0
MRC:
main(){
__block MJPerson *person = [[MJPerson alloc] init];
MJBlock block = [^{
NSLog(@"%p", person);
} copy];
[person release];
block();
[block release];
}
2021-04-11 23:29:27.590414+0800 Interview01-__block[19583:10326096] -[MJPerson dealloc]
2021-04-11 23:29:27.591165+0800 Interview01-__block[19583:10326096] 0x10051bae0
MRC下person被释放了block内部还能访问,说明是弱引用
总结;block对__block变量(基本数据类型、对象类型)结构体一定是强引用。
15、__block变量的内存管理
栈block不会对__block变量(基本数据类型、对象类型)强引用;
当栈block被copy到堆上时,调用block内部copy函数对__block变量(基本数据类型、对象类型)强应用,
当栈block被移除时,调用block内部dispose函数对__block变量(基本数据类型、对象类型)release.
__Block_byref_id_object_copy、__Block_byref_id_object_dispose是对__Block_byref_weakPerson_0内的MJPerson *__weak weakPerson。
(1)对象类型:__main_block_desc_0里copy()、dispose(),对对象类型内存管理
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); //copy函数在desc结构体里
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc; ///////////////////看这里///////////////////////
MJPerson *__strong person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
(2)__block 基本类型:__main_block_desc_0里copy()、dispose(),对__Block_byref_age_0内存管理
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age; /////////////////////看这里///////////////////////
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *p;
(struct)__Block_byref_age_0 *age; // by ref ///////////////////block对__block 基本类型强引用,C++中可以省略struct///////////////////////
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {////////////////////////////__main_block_copy_0///////////////////////////////
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_0 { //////////////copy、dispose在desc里//////////////
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
(3)__block 对象类型:__main_block_desc_0里copy()、dispose(),对__Block_byref_age_0内存管理,
__Block_byref_weakPerson_0里的__Block_byref_id_object_copy、__Block_byref_id_object_dispose对__Block_byref_weakPerson_0内的MJPerson *__weak weakPerson内存管理;
struct __main_block_desc_0 {//////////////////////////////__main_block_desc_0///////////////////////////////
size_t reserved;
size_t Block_size;
void (*copy)(void);
void (*dispose)(void);
};
struct __Block_byref_weakPerson_0 {////////__Block_byref_weakPerson_0//////////////////
void *__isa; // 8
__Block_byref_weakPerson_0 *__forwarding; // 8
int __flags; // 4
int __size; // 4
void (*__Block_byref_id_object_copy)(void*, void*); // 8 ///__Block_byref_id_object_copy//////////
void (*__Block_byref_id_object_dispose)(void*); // 8 ///__Block_byref_id_object_dispose//////////
MJPerson *__weak weakPerson;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // by ref/////////////////////////////////
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
16.理解__forwarding指针:
栈上__block变量结构体的__forwarding 指向自己本身;
发生复制后:此时
栈上__block变量结构体的__forwarding 指向堆上__block变量结构体;
堆上__block变量结构体的__forwarding 指向自己本身;
总结:没发生复制__forwarding指向自己,发生了复制__forwarding指向堆上__block变量的结构。体,即没复制是自己,复制了就是堆区的结构体。
17.__weak和__unsafe_unretained区别?
__weak不会产生强引用,指向的对象销毁时,会自动让指针置为nil
__unsafe_unretained不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。
18.block循环引用的解决方式有哪些?
ARC:__weak、__block 、__unsafe_unretained(不建议用)。
MRC:__block 、__unsafe_unretained(不建议用)。MRC不支持__weak的
__weak typeof(self) weakSelf = self;
__unsafe_unretained id weakSelf = self;
self.block = ^{
printf(@"%p",weakSelf);
}
__block id weakSelf = self;
self.block = ^{
printf(@"%p",weakSelf);
weakSelf = nil;
}
self.block()
缺点:不调用self.block()会产生循环引用,进而造成内存泄漏。
19.block在修改NSMutableArray,需不需要添加__block?不需要。
NSMutableArray *array = [NSMutableArray array];
person.block = ^{
[arr addObject:@"1"];
} ;
20.__block作用是什么?注意点?
修改局部变量的值,在MRC下block不会对__block变量结构体强引用。
21.block属性修饰词为什么是copy?使用block有哪些注意点?
copy在堆上可以控制block的生命周期,对block进行内存管理。
注意循环引用。
网友评论