Dear All 这节我们来学习block知识 ,废话不说 、让我们直奔主题
- __block关键字的作用 (基本数据类型)
/*对于基本数据类型的变量
如果是局部的:
在声明前不加__block,那么在block中用到的该变量是对原来值的一个copy(在给block块分配内存空间的时候),该block是不允许更改该变量的(不允许写)。
所以编译时候会报错( variable is not assignable (missing __block type specifier ))。
在声明前加__block,在block使用的过程中不会对原来的值进行copy,可以直接读写该变量
如果是全局的或是静态的
那么不会copy该变量的值
*/
//int static sum = 10;
- (void)__blockTest{
//int sum = 5; //( variable is not assignable (missing __block type specifier ))
__block int sum = 5;
NSLog(@"1---%p --- %d", &sum, sum);
sum = 10;
void (^sumBlock) (int m, int n) = ^(int m, int n) {
sum = m + n;
NSLog(@"2---%p --- %d", &sum, sum);
};
sum = 15;
sumBlock(1, 2);
NSLog(@"3---%p --- %d", &sum, sum);
__block NSString *name = @"小明";
NSLog(@"1---%@---%p", name, &name);
void (^stringBlock) (void) = ^(void) {
name = @"小丽";
NSLog(@"2---%@---%p", name, &name);
};
name = @"ls";
stringBlock();
NSLog(@"3---%@---%p", name, &name);
}
/*
非ARC 输出结果
11---0x7fff5aeb3888 --- 5
22---0x7fff5aeb3888 --- 3
33---0x7fff5aeb3888 --- 3
1---小明---0x7fff5aeb3828
2---小丽---0x7fff5aeb3828
3---小丽---0x7fff5aeb3828
*/
/*
ARC 输出结果
1---0x7fff5b2fa828 --- 5
2---0x7f9a42783d98 --- 3
3---0x7f9a42783d98 --- 3
1---小明---0x7fff5b2fa7c8
2---小丽---0x7f9a42728478
3---小丽---0x7f9a42728478
*/
总结:
如果想在block内修改某局部变量需加__block, MRC 环境下block在使用过程中不会对原来值进行copy,可以直接修改该变量 ,ARC环境下会对原值进行copy,内存地址也发生变化。
block可以直接修改 全局和静态变量 ,不会copy该变量的值。(代码中没给出、大家可以自己打印看下) 。
2.__block关键字的作用 (指针类型的变量)
- (void)__blockObjTest
{
//对于指针类型的变量
//如果不加__block,block中是对(原来指针的copy),也就是说有两个不同的指针,指向同一个对象,在block中可以更改对象的属性值,但是不可以更改对象(可以对指向的对象改变其属性,不能把原来的线断掉再建立一个新线)
//如果添加__block,block中不会对原来的指针进行copy,所以可以更改属性,也可以更改其值
//如果是全局的,或者是静态的,则不会copy指针
__block People *people = nil;
people = [[People alloc] init];
people.name = @"zhangsan";
NSLog(@"1---%@---%p", people, &people);
void (^peopleBlock) (void) = ^(void) {
NSLog(@"2---%@---%p", people, &people);
people.name = @"wangwu";
/*
people = [[People alloc] init];
people.name = @"zhaoliu";
*/
};
people.name = @"lisi";
peopleBlock();
NSLog(@"3---%@---%p", people, &people);
}
/*
MRC 环境下打印结果:
//不使用 __block
1---<People: 0x7f9c4bc11fd0>---0x7fff57f1c708
2---<People: 0x7f9c4bc11fd0>---0x7fff57f1c6f8
3---<People: 0x7f9c4bc11fd0>---0x7fff57f1c708
//使用 __block
1---<People: 0x7f8c72775ce0>---0x7fff59240708
2---<People: 0x7f8c72775ce0>---0x7fff59240708
3---<People: 0x7f8c72607320>---0x7fff59240708
*/
/*
ARC 环境下打印结果:
//不使用 __block
1---<Person: 0x7f84a9ff6280>---0x7fff52527828
2---<Person: 0x7f84a9ff6280>---0x7f84a9e07080
3---<Person: 0x7f84a9ff6280>---0x7fff52527828
//使用 __block
1---<Person: 0x7fb090704970>---0x7fff5a905828
2---<Person: 0x7fb090704970>---0x7fb090553938
3---<Person: 0x7fb090704970>---0x7fb090553938
*/
总结:
- 不加__block, MRC 和 ARC block中都是对(原来指针的copy),也就是有两个不同的指针,指向同一个对象。
- 使用__block ,
- MRC环境block中不会对原来的指针进行copy,所以可以更改属性,也可以更改对象本身 。
- ARC环境则是对原对象的copy,内存地址也发生变化。
- block可以直接修改指针类型 全局和静态变量 ,不会copy该变量的值。(代码中没给出、大家可以自己打印看下) 。
4.__weak关键字的作用
接下来我们学习一下 weak 关键字,先上代码
- (void)weakTest
{
Person *p = [[Person alloc]init];
p.name = @"myObject";
NSLog(@"1---%@---%p--%@", p, &p,p.name);
__weak Person *weakObj = p;
NSLog(@"2---%@---%p--%@", weakObj, &weakObj,weakObj.name);
void(^testBlock)() = ^(){
NSLog(@"3---%@---%p--%@", weakObj, &weakObj,weakObj.name);
// NSLog(@"3---%@---%p--%@", p, &p,p.name);
};
testBlock();
p = nil; // 这边值nil 用来判断block是否复制了对象
testBlock();
}
/*
ARC 环境下打印结果:(MRC 是没有__weak关键字的)
//不使用 __weak
1---<Person: 0x7fbf82505070>---0x7fff5ea9b828--myObject
2---<Person: 0x7fbf82505070>---0x7fff5ea9b820--myObject
3---<Person: 0x7fbf82505070>---0x7fbf82417330--myObject
3---<Person: 0x7fbf82505070>---0x7fbf82417330--myObject
//使用 __weak
1---<Person: 0x7f87d3f0ca90>---0x7fff5811a828--myObject
2---<Person: 0x7f87d3f0ca90>---0x7fff5811a820--myObject
3---<Person: 0x7f87d3f0ca90>---0x7f87d3e0df30--myObject
3---(null)---0x7f87d3e0df30--(null)
*/
总结:
不使用 __weak, p = nil 后block块内打印出的对象仍不为空,说明block中都是对原对象的copy。
使用 __weak p = nil 后person对象为nil ,说明block是对(原来指针的copy),也就是有两个不同的指针,指向同一个对象,对象释放后 weakObj 也不在持有, 并会被置nil 防止野指针报错。
__weak解决循环引用问题。
typedef void(^Block1)(NSString *str );
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,copy)Block1 block;
@end
- (void)__weakTest
{
// __weak typeof(self)weakSelf = self;
self.block = ^(NSString *name){
// NSLog(@"arr:%@", self.arr);
// [self weakTest];
// p.name = @"haha";
};
self.block(@"123");
}
/*
block在copy时都会对block内部用到的对象进行强引用(ARC)或 者retainCount增1(非ARC)。
在ARC与非ARC环境下对block使用不当都会引起循环引用问题 。
一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,
简单说就是self.block = ^(Type var){
[self dosomething];
或者 self.otherVar = XXX;
或者 _otherVar = ...
};
block的这种循环引用会被编译器捕捉到并及时提醒
*/
结论:
- 某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身, block的这种循环引用会被编译器捕捉到并及时提醒。解决方法
__weak typeof(self)weakSelf = self;
__weak 可能产生的问题分析........ 试想 weakSelf 指针是没有对象持有权的,那么外部对象被提前释放了怎么办?block内部的执行岂不是会出错 ?这个问题又当如何解决呢? 神奇的 strong
关键字来了, 先看代码
-(void)strongTest
{
Person* p = [[Person alloc]init];
p.name = @"myObject";
NSLog(@"1---%@---%p--%@", p, &p,p.name);
__weak Person *weakObj = p;
NSLog(@"2---%@---%p--%@", weakObj, &weakObj,weakObj.name);
void(^testBlock)() = ^(){
__strong Person *strongObj = weakObj;
NSLog(@"3---%@---%p--%@", strongObj, &strongObj,strongObj.name);
};
testBlock();
p = nil;
testBlock();
}
/*
1---<Person: 0x7f9410f7e7f0>---0x7fff5e28f828--myObject
2---<Person: 0x7f9410f7e7f0>---0x7fff5e28f820--myObject
3---<Person: 0x7f9410f7e7f0>---0x7fff5e28f748--myObject
3---(null)---0x7fff5e28f748--(null)
*/
发现 __strong 修饰的对象仍被置nil 了 怎么回事呢 ?? 接着看.....
-(void)strongTestC
{
Person* p = [[Person alloc]init];
p.name = @"myObject";
__weak Person *weakObj = p;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong Person *strongObj = weakObj;
NSLog(@"1---%@---%p--%@", strongObj, &strongObj,strongObj.name); //先打印这行
sleep(3); // 睡眠三秒确保 p 被置 nil 后执行接下来的代码
NSLog(@"3---%@---%p--%@", weakObj, &weakObj,weakObj.name);
NSLog(@"3---%@---%p--%@", strongObj, &strongObj,strongObj.name);
});
sleep(1); //睡眠1秒让异步线程block块执行
p = nil; //执行过程中将 p 对象置 nil
NSLog(@"2---%@---%p--%@", weakObj, &weakObj,weakObj.name);
sleep(4); //异步线程结束后 再打印出 person 对象
NSLog(@"4---%@---%p--%@", weakObj, &weakObj,weakObj.name);
}
/*
1---<Person: 0x7f828248f120>---0x700000116df8--myObject
2---<Person: 0x7f828248f120>---0x7fff57f35820--myObject
3---<Person: 0x7f828248f120>---0x7f82824aa1c0--myObject
3---<Person: 0x7f828248f120>---0x700000116df8--myObject
4---(null)---0x7fff57f35820--(null)
*/
结论:
p = nil
后由__strong
修饰的对象仍然存在。
说明,再block执行过程中,如果对象用 __strong 修饰 block内部依然会继续强引用它 。
那么上面的例子也就不奇怪了,因为下面的代码是后续执行的,所以打印出结果为 nil 。
block 内部的 __strong 会在执行期间进行强引用操作,保证在 block 内部 strongObj 始终是可用的。这种写法非常巧妙,既避免了循环引用的问题,又可以在 block 内部持有该变量
我们平时在使用时,常常先判断 strongObj 是否为空,然后再执行后续代码,如下方式
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong Person *strongObj = weakObj;
if (strongObj) {
//
}
});
6 . block变量定义时为什么用copy关键字
默认情况下,block是存档在栈中,可能被随时回收,故需要copy操作。这也就是我们在定义block的时候用得时copy (arc 下也可以用strong), 而不是weak等。
原因是Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存
具体请自行谷歌(网上太多了)。
总结 ,这节主要回顾了__weak __block __stong
的用法 ,内容还是挺复杂的 要仔细观看哦 Learning together is Better