__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所以地址改變。