__block关键字的实现原理
__block加与不加的区别简单来说就是不加的时候做赋值操作,加了之后传地址。
因此如果不加,block中的变量和block外的局部变量地址不同。
如果加block中的变量和block外的局部变量地址相同。
例如:
{
int a=0;
void (^function)()=^(){
NSLog(@"%d",a);
};
a = 20;
function();
}
打印:
2016-08-08 19:56:32.365 test[65147:434644] 0
加了__block之后
{
__block int a=0;
void (^function)()=^(){
NSLog(@"%d",a);
};
a = 20;
function();
}
打印:
2016-08-08 19:57:03.094 test[65224:435626] 20
这里主要想说明下__block的实现原理,先来看两段代码:
例子1
-(void)testBlock
{
__block int a=0;
NSLog(@"a address %p",&a);
void (^function)()=^(){
NSLog(@"a address %p",&a);
a = 100;
NSLog(@"1 %d",a);
};
a = 20;
function();
NSLog(@"a address %p",&a);
NSLog(@"2 %d",a);
}
输出:
2016-08-08 19:09:19.988 test[59876:394725] a address 0xbfffc1f8
2016-08-08 19:09:19.988 test[59876:394725] a address 0xbfffc1f8
2016-08-08 19:09:19.989 test[59876:394725] 1 100
2016-08-08 19:09:19.989 test[59876:394725] a address 0xbfffc1f8
2016-08-08 19:09:19.989 test[59876:394725] 2 100
例子2
-(void)testBlock
{
testClass *tctemp = [[testClass alloc] init];
self.tc = tctemp;
[tctemp release];
__block int a=0;
NSLog(@"a address %p",&a);
void (^function)()=^(){
NSLog(@"a address %p",&a);
a = 100;
NSLog(@"1 %d",a);
};
a = 20;
function();
self.tc.fun = function;
self.tc.fun();
NSLog(@"2 %d",a);
NSLog(@"a address %p",&a);
}
输出:
2016-08-08 19:13:59.914 test[60525:400152] a address 0xbff611f0
2016-08-08 19:13:59.915 test[60525:400152] a address 0xbff611f0
2016-08-08 19:13:59.915 test[60525:400152] 1 100
2016-08-08 19:13:59.915 test[60525:400152] a address 0x7cb31820
2016-08-08 19:13:59.916 test[60525:400152] 1 100
2016-08-08 19:13:59.916 test[60525:400152] 2 100
2016-08-08 19:13:59.916 test[60525:400152] a address 0x7cb31820
两代代码很相似 唯一的区别是后一段代码将function赋值给了一个成员变量,而这段代码之后地址就发生了变化。
原理:
__block变量编译之后为一个结构:
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
NSInteger a;
};
__Block_byref_val_0 *__forwarding;指向自己。如图:
self.tc.fun = function;语句执行完毕后,原本在栈上的__Block_byref_val_0被拷贝了一份到堆上,因为栈上的随时可能被释放。
而内存中堆和栈上的关系如下:
如此无论访问的是栈里的__block还是堆里的__block都是以val->__forwarding的形式访问,因此访问的都是堆上的__block所以地址改变。