Block结构
block的代码是内联的,效率高于函数调用
block对于外部变量默认是只读属性,即在Block函数体里面不能改变Block之外的变量,只可以读取。否则会报错。
block被Objective-C看成是对象处理,其实真实的存储情况是一个结构体的形式
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
1.这里主要是研究下block的调用顺序
typedef void(^myBlockTest)(int);//声明一个block块(block 的重命名)
-(void)testBlock:(NSString *)str andBlock:(myBlockTest)block
{
3. NSLog(@"str===%@",str);
4. //block(100); //将100传给参数intParameter,回调block的实现函数
//block块的回调,当这行代码不注释时,
控制台输出:NonAtomicTest[2841:113472] str===我是字符窜
NonAtomicTest[2841:113472] 实现block
NonAtomicTest[2898:116086] 大于10
//当这行代码注释时
控制台输出:NonAtomicTest[2841:113472] str===我是字符窜
}
-(void)test1
{
1. NSString *str = @"我是字符窜";
2. [self testBlock:str andBlock:^(int intParameter) {
// str = @"1111”;
//这行代码是错误的,因为这个大括号里面是block块的实现,它只可以对block块的参数intParameter赋值,不可以对testBlock:andBlock:这个函数的参数str赋值。
5. NSLog(@"实现block");
6. if (intParameter>10) {
NSLog(@"大于10");
}
else
{
NSLog(@"不大于10");
}
}];
}
这个例子已经标出调用顺序,这里将block块可以看作是内联函数
补充:如何在block中修改外部变量
__block int a = 0;
void (^blockTest)(void) = ^{
a = 1;
}
blockTest();
2.block中__strong和__weak的使用
问:为什么使用weakSelf
答:通过 clang -rewrite-objc 源代码文件名 将代码转为c++代码(实质是c代码),可以看到block是一个结构体,它会将全局变量保存为一个属性(是_ _strong的),而self强引用了block这会造成循环引用。所以需要使用__weak修饰的weakSelf。self 持有block,block持有self。
问:在block中weak 和strong的使用场景是什么?
答:(摘自http://www.jianshu.com/p/36342264d6dfApple )官方的建议是,传进 Block 之前,把 ‘self’ 转换成 weak automatic 的变量,这样在 Block 中就不会出现对 self 的强引用。如果在 Block 执行完成之前,self 被释放了,weakSelf 也会变为 nil。
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething];
});
clang 的文档表示,在 doSomething 内,weakSelf 不会被释放。但,下面的情况除外:
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething];
[weakSelf doOtherThing];
});
在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong typeof(self) strongSelf = strongSelf;
[strongSelf doSomething];
[strongSelf doOtherThing];
});
如果仅仅使用__weak去修饰变量,当别处把变量释放后,block中该变量也会被释放掉。
__strong 确保在 Block 内,strongSelf 不会被释放。当加上修饰符strong时,当别处(别的block 内)把变量释放掉,但调用该变量的block如果仍然没有执行结束,那么系统就会等待block执行完成后再释放,对该变量在block中的使用起到了保护作用。当block执行结束后会自动释放掉。
总结
arc
>当在block内需要访问self相关属性的时候,添加 __weak 防止循环引用。
>当block内的变量不仅仅在block中使用的时候,为了防止变量提前释放,添加 __strong。
>当block内需要引用外部变量的时需要添加 __block ,因为局部变量出了范围就会释放,但是block调用的时机是不确定的。所以使用 __ block 将block复制到堆区,引用的变量也在堆区,以后的操作也都在这里进行。
>但是__block 本身无法避免循环引用的问题,所以我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。
注:__block有一点:这只是限制在ARC环境下。在非arc下,__block是可以避免引用循环的。
block中各种情况的出现及解决办法
#define BLog(prefix,obj) {NSLog(@"位置和指针变量名:%@ ,指针内存地址:%p, 指针值:%p ,指向的对象:%@ ",prefix,&obj,obj,obj);}
// 强引用
- (void)blockVariableStrongReferenceTest
{
NSLog(@"\n");
NSObject *obj = [[NSObject alloc] init];//+1
BLog(@"StrongRef obj",obj);
void(^testBlock)()= ^(){
BLog(@"StrongRef in block",obj);//+1Block中obj指针已经不是外部的obj指针了,它是外部变量obj的拷贝,内存引用计数加一
};
testBlock();
// Block外部尝试将obj置为nil
obj = nil;//-1
testBlock(); // 第二次调用block
}
解释:block内部的obj 指针是外部obj指针的拷贝,有2个指针指向同一个NSObject对象,(没有重新分配空间)但只将外部的obj指针置为nil,NSObject对象的引用计数不为0,无法回收。
// 弱引用
- (void)blockVariableWeakReferenceTest
{
NSObject *obj = [[NSObject alloc] init];//+1
__weak NSObject *weakObj = obj;//+0 weakObj和obj是2个不同的指针,指向同一块内存地址,但是因为是弱引用,所以引用计数不改变
BLog(@"WeakRef weakObj", weakObj);
void(^testBlock)()= ^(){
BLog(@"weakObj in block",weakObj);
};
testBlock();
obj = nil; // -1=0
testBlock();//
}
解释:在block中__weak声明的指针去引用对象 可以避免循环引用的问题,但是当外部对象被释放了,block 内部会访问不到这个对象.
//多线程时Block生命周期内对象安全
- (void)blockVariableMutiThreadTest
{
NSObject *obj = [[NSObject alloc]init]; //obj强引用,<NSObject: 0x7f9413c1c040>对象引用计数+1,=1
BLog(@"obj", obj);
__weak NSObject *weakObj = obj;//weakObj弱引用,<NSObject: 0x7f9413c1c040>对象引用计数不变,=1
BLog(@"weakObj-0", weakObj);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong NSObject *strongObj = weakObj; //strongObj强引用,<NSObject: 0x7f9413c1c040>对象引用计数+1,=2
sleep(3);
BLog(@"weakObj - block", weakObj);
BLog(@"strongObj - block", strongObj);
});
sleep(1);
obj = nil; //obj被置为nil,<NSObject: 0x7f9413c1c040>对象引用计数-1,=1
BLog(@"weakObj-1", weakObj); //没被释放
sleep(4); //block在异步线程中执行完毕(在另一块内存中执行),block内存被释放,<NSObject: 0x7f9413c1c040>对象引用计数-1,=0;ARC开始把0x7f9413c1c040对象内存回收,把弱引用weakObj置为nil
BLog(@"weakObj-2", weakObj);
}
总结 :
多线程的时候,在 block 外部用__weak声明的变量指向一个对象, 通过把weak声明的变量值赋值给block内部的```strong变量,实现在block内对该对象进行强引用,这样可以在block生命周期内保留该对象不被释放,在block生命周期结束后,对象内存被释放。
(void)blockVariable
{
NSObject *obj = [[NSObject alloc]init]; //指向的对象:<NSObject: 0x7fb4039063b0>
BLog(@"obj",obj);
__block NSObject *blockObj = obj; //blockObj指向对象:0x7fb4039063b0
obj = nil;
BLog(@"blockObj -1",blockObj); //blockObj,0x7fff52365c90
void(^testBlock)() = ^(){
BLog(@"blockObj - block",blockObj); //blockObj,0x7fb401c7d7f8,指向的对象:<NSObject: 0x7fb4039063b0>
NSObject *obj2 = [[NSObject alloc]init]; // obj2 ,0x7fff52365bc8,指向的对象:<NSObject: 0x7fb401c83c40>
BLog(@"obj2",obj2);
blockObj = obj2;
BLog(@"blockObj - block",blockObj); //blockObj,0x7fb401c7d7f8,指向对象:<NSObject: 0x7fb401c83c40>
};
NSLog(@"%@",testBlock); //block 的地址 <__NSMallocBlock__: 0x7fb401c07d20>
BLog(@"blockObj -2",blockObj); //blockObj地址发生变化,0x7fb401c7d7f8,/指向的对象:<NSObject: 0x7fb4039063b0>
testBlock();
BLog(@"blockObj -3",blockObj); //blockObj,0x7fb401c7d7f8,指向对象:<NSObject: 0x7fb401c83c40>
}
分析 :
第3处日志打印了一个testBlock对象,blockObj的地址发生变化。此时,block对象 从栈拷贝到堆上,__block变量blockObj,也被拷贝到堆上。block对象拥有blockObj指针指向的对象。注意:这是个强引用哦。
关注4到8 处日志,用__block关键字声明blockObj指针后,block内外的变量blockObj都是0x7fa084905838,也就是block内外的blockObj指针是同一个指针。
block内部改变 blockObj指针指向的对象,改动在 block外部可见。
常用的出现循环使用:(只要你在block里用到了self所拥有的东西!)
block在copy时都会对block内部用到的对象进行强引用(ARC)或者retainCount增1(非ARC)。在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是
self.someBlock = ^(Type var){
[self dosomething];
或者self.otherVar = XXX;
或者_otherVar = ...XXX;
};
//block的这种循环引用会被编译器捕捉到并及时提醒。
- 即使在你的block代码中没有显式地出现"self",也会出现循环引用!只要你在block里用到了self所拥有的东西!
- 但对于这种情况,我们无法通过加__weak声明或者__block声明去禁止block对self进行强引用或者强制增加引用计数。但我们可以通过其他指针来避免循环引用,具体是这么做的:
__weak typeof(self) weakSelf = self;
self.blkA = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;//加一下强引用,避免weakSelf被释放掉
NSLog(@"%@", strongSelf->_xxView); //不会导致循环引用.
};
self.arr
1)ARC环境下:
ARC环境下可以通过使用_weak声明一个代替self的新变量代替原先的self,我们可以命名为weakSelf。通过这种方式告诉block,不要在block内部对self进行强制strong引用:(如果要兼容ios4.3,则用__unsafe_unretained代替__weak,不过目前基本不需考虑这么low的版本)
self.arr = @[@111, @222, @333];
__weak typeof(self) weakSelf=self;
self.block = ^(NSString *name){
NSLog(@"arr:%@", weakSelf.arr);
};
2)MRC环境下:解决方式与上述基本一致,只不过将__weak关键字换成__block即可,这样的意思是告诉block:小子,不要在内部对self进行retain了!
委托delegate
一字诀:声明delegate时请用assign(MRC)或者weak(ARC),
对block想要深入研究的 :http://www.jianshu.com/p/ee9756f3d5f6 http://www.cocoachina.com/ios/20170527/19308.html
https://yq.aliyun.com/articles/62662