__block加與不加的區(qū)別簡單來說就是不加的時候做賦值操作钦睡,加了之后傳地址憔鬼。
因此如果不加吉嚣,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的實(shí)現(xiàn)原理夫凸,先來看兩段代碼:
例子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
兩代代碼很相似 唯一的區(qū)別是后一段代碼將function賦值給了一個成員變量浑劳,而這段代碼之后地址就發(fā)生了變化。
原理:
__block變量編譯之后為一個結(jié)構(gòu):
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;語句執(zhí)行完畢后魔熏,原本在棧上的__Block_byref_val_0被拷貝了一份到堆上衷咽,因?yàn)闂I系碾S時可能被釋放。
而內(nèi)存中堆和棧上的關(guān)系如下:
如此無論訪問的是棧里的__block還是堆里的__block都是以val->__forwarding的形式訪問蒜绽,因此訪問的都是堆上的__block所以地址改變镶骗。