问题一:block中要注意的问题
底层的东西:
http://www.cocoachina.com/ios/20150106/10850.html
http://www.cocoachina.com/ios/20150109/10891.html
http://www.jianshu.com/p/a5dd014edb13
自己参考:
https://my.oschina.net/u/1432769/blog/390401
https://www.mgenware.com/blog/?p=503
http://blog.csdn.net/wenxiangjiang/article/details/51387629
https://my.oschina.net/leejan97/blog/268536
http://blog.csdn.net/majiakun1/article/details/38702977
https://www.zhihu.com/question/30779258
Typedef用法:
http://blog.csdn.net/hamasn/article/details/7644425
在block中只能对变量进行操作,而不能进行赋值。否则,要用__block修饰。
声明一个Block类型变量 ,并加上typedef修饰符:
typedef void(^Blo)(NSString *s1,UIColor *c);
页面B的.h文件中定义了这样一个Block执政,然后声明了一个变量,像这样:
typedef void(^Blo)(NSString *s1,UIColor *c);
@property (nonatomic, copy) Blo block;
然后我们在页面A当中有这么一段代码:
ViewController *b = [[ViewController alloc]init];
__weak ViewController *wself = self;
b.block = ^(NSString *s1,UIColor *c){
NSLog(@"%@",s1);
wself.view.backgroundColor = c;
};
[self.navigationController pushViewController:b animated:true];
然后在页面B的任意地方我们调用block变量,像这样:
self.block(@"str",[UIColor redColor]);
都会在A页面中调用B页面传过来的参数,在A页面进行操作,对控制器A进行改变,这样的做法通常用做 控制器 反向传值。
在这里有一点需要注意就是Block的使用引起的循环引用。如果在Block中使用附有__strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,改对象为Block所有。这样容易引起循环引用,从而发生内存泄漏,然而我们只需要保证当前控制器也就是self在需要释放的时候正确释放就可以,所以我们再来看上面那段代码:
__weak ViewController *wself = self;
我们定义一个wself变量并加上__weak修饰符,在Block代码块中,所有需要self的地方都用wself来替代。这样就不会增加引用计数,所以Block持有self对象也就不会造成循环引用,从而造成内存泄漏。
注意:是self和block相互持有才会造成循环引用,self持有block不一定会造成。
Block 是 匿名函数指针,是指向匿名函数的指针,不是匿名函数。
除非同步调用,不推荐在block内和之后同时引用同一个变量,更不要修改,没必要去搞。
Block引用的变量会被复制,标记block的变量会被跟踪变化并复制回原栈的产量,同步调用时很实用。
ARC情况下
1.如果用copy修饰Block,该Block就会存储在堆空间。则会对Block的内部对象进行强引用,导致循环引用。内存无法释放。
解决方法:
新建一个指针(__weak typeof(Target) weakTarget = Target )指向Block代码块里的对象,然后用weakTarget进行操作。就可以解决循环引用问题。
2.如果用weak修饰Block,该Block就会存放在栈空间。不会出现循环引用问题。
MRC情况下
用copy修饰后,如果要在Block内部使用对象,则需要进行(__block typeof(Target) blockTarget = Target )处理。在Block里面用blockTarget进行操作。
必须强调以上例子是基于ARC的,如果是MRC的话,__block反而是不增加引用计数的
1、对于block外的变量引用,block默认是将其复制到其数据结构中来实现访问的。通过block进行闭包的变量是const的。也就是说不能在block中直接修改这些变量。
2、对于用__block修饰的外部变量引用,block是复制其引用地址来实现访问的。
总结一下Block到底是什么、用来干什么:
C++中的Struct(本文未提到)。
用来弥补iOS中函数传递的功能。
他是一段代码块的内存的指针。
和delegate一样的功能,但是显的更加简洁。
block有几种不同的类型,每种类型都有对应的类,上述中isa指针就是指向这个类。这里列出常见的三种类型:
_NSConcreteGlobalBlock:全局的静态block,不会访问任何外部变量,不会涉及到任何拷贝,比如一个空的block。例如:
include int main()
{
^{ printf(“Hello, World!\n”); } ();
return 0;
}
_NSConcreteStackBlock:保存在栈中的block,当函数返回时被销毁。例如:
include int main()
{
char a = ‘A’;
^{ printf(“%c\n”,a); } ();
return 0;
}
_NSConcreteMallocBlock:保存在堆中的block,当引用计数为0时被销毁。该类型的block都是由_NSConcreteStackBlock类型的block从栈中复制到堆中形成的。例如下面代码中,在exampleB_addBlockToArray方法中的block还是_NSConcreteStackBlock类型的,在exampleB方法中就被复制到了堆中,成为_NSConcreteMallocBlock类型的block:
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = ‘B’;
[array addObject:^{
printf(“%c\n”, b);
}];
}
void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
总结一下:
_NSConcreteGlobalBlock类型的block要么是空block,要么是不访问任何外部变量的block。它既不在栈中,也不在堆中,我理解为它可能在内存的全局区。
_NSConcreteStackBlock类型的block有闭包行为,也就是有访问外部变量,并且该block只且只有有一次执行,因为栈中的空间是可重复使用的,所以当栈中的block执行一次之后就被清除出栈了,所以无法多次使用。
_NSConcreteMallocBlock类型的block有闭包行为,并且该block需要被多次执行。当需要多次执行时,就会把该block从栈中复制到堆中,供以多次执行。
上文中提到,如果我们想要在以后继续使用某个block,就必须要对该block进行拷贝操作,即从栈空间复制到堆空间。所以拷贝操作就需要调用Block_copy()函数,block的descriptor中有一个copy()辅助函数,该函数在Block_copy()中执行,用于当block需要拷贝对象的时候,拷贝辅助函数会retain住已经拷贝的对象。
既然有有copy那么就应该有release,与Block_copy()对应的函数是Block_release(),它的作用不言而喻,就是释放我们不需要再使用的block,block的descriptor中有一个dispose()辅助函数,该函数在Block_release()中执行,负责做和copy()辅助函数相反的操作,例如释放掉所有在block中拷贝的变量等。