__block修饰符的作用域问题

时间:2021-07-15 16:47:26

~~~~我的生活,我的点点滴滴!!







我们知道,当在一个Block中想要访问这个Block之外的变量时,可以直接调用,但当我们想在Block中修改这个成员变量时,需要为

个外部变量使用__block关键字进行声明,前面我们使用__block声明了一个基本数据类型int,那么我们在Block内成功将n改为了2:


#import <Foundation/Foundation.h>

typedef int(^Sum)(int, int);

int main(int argc, const char * argv[])
{

@autoreleasepool {

// insert code here...
//NSLog(@"Hello, World!");

int n = 1;

Sum sum = ^(int a, int b)
{
//n = 2;

NSLog(@"n = %d", n);

return (a+b) * n;
};

NSLog(@"n = %d, result = %d", n, sum(5, 6));

}
return 0;
}


看看运行结果:

2015-04-29 08:23:12.186 blocktest[10816:303] n = 1
2015-04-29 08:23:12.188 blocktest[10816:303] n = 1, result = 11


不对啊!block里面打印的n=2,而外部打印的却是1,这是为什么呢?这说明使用__block修饰后的基本变量在block中的修改只对block中

有用,并不会真正改变这个变量的值!

此时会有人问:如果不把n声明为__block会怎么样了?如果没有添加关键字__block,block里面也能捕获到n的存在,只不过在里面不能更改n的值,

即此时要注释掉 n = 2,不然就会报错。

那么,对于基本变量如此,对于对象变量呢?我们再来试试:


新建一个测试对象:


TestObject.h

@interface TestObject : NSObject

@property (nonatomic, retain) NSString * name;

@end


TestObj.m

#import "TestObject.h"

@implementation TestObject

- (void)dealloc {
NSLog(@"%@被销毁了!",self);
[_name release];
[super dealloc];
}

@end




main.m


#import <Foundation/Foundation.h>
#import "TestObject.h"

typedef int(^Sum)(int, int);

int main(int argc, const char * argv[])
{

@autoreleasepool {

// insert code here...
//NSLog(@"Hello, World!");

int n = 1;

Sum sum = ^(int a, int b)
{
//n = 2;

NSLog(@"n = %d", n);

return (a+b) * n;
};

NSLog(@"n = %d, result = %d", n, sum(5, 6));

__block TestObject *obj = [[TestObject alloc] init];

obj.name = @"test out block";

NSString* (^Name)(TestObject *obj) = ^(TestObject *obj)
{
obj.name = @"test in block";

return obj.name;
};

NSLog(@"%@", obj.name);
NSLog(@"%@", Name(obj));
//加不加__block 都输出 "test in block" 说明改变了其值,block里面此用的是指针地址
NSLog(@"%@", obj.name);
}
return 0;
}



这里我们在main函数中定义了一个TestObject对象,并调用setter方法更改name的属性。然后将TestObject对象传入一个名为Name的block中,

在block再调用setter方法修改name属性。再在外部打印返回值和TestObject对象的getter方法的值:结果如下:

2015-04-29 08:23:12.186 blocktest[10816:303] n = 1
2015-04-29 08:23:12.188 blocktest[10816:303] n = 1, result = 11
2015-04-29 08:23:12.188 blocktest[10816:303] test out block
2015-04-29 08:23:12.189 blocktest[10816:303] test in block
2015-04-29 08:23:12.189 blocktest[10816:303] test in block
Program ended with exit code: 0


结果外部和内部的都是一样的,说明传入给block中的是对象的指针,指向的是同一个地址。既然传入的是同一个地址,那么说明这个对象指针

其实是没有变的,所以,其实不需要__block也可以的:


#import <Foundation/Foundation.h>
#import "TestObject.h"

int main(int argc, const char * argv[])
{
@autoreleasepool {
TestObject *obj = [[[TestObject alloc] init] autorelease];
obj.name = @"XCoder";

NSString * (^Name)(TestObject *) = ^(TestObject * myObj) {
myObj.name = @"XCoder Studio";
return myObj.name;
};

NSLog(@" In Block:%@", Name(obj));
NSLog(@"Out Block:%@", obj.name);
}
return 0;
}



这样运行结果也是正确的!同样能得到上面的结果。


然而,如果我指针重新指向一个新的TestObject对象呢?

#import <Foundation/Foundation.h>
#import "TestObject.h"

typedef int(^Sum)(int, int);

int main(int argc, const char * argv[])
{

@autoreleasepool {

// insert code here...
//NSLog(@"Hello, World!");

// int n = 1;
//
// Sum sum = ^(int a, int b)
// {
// //n = 2;
//
// NSLog(@"n = %d", n);
//
// return (a+b) * n;
// };
//
// NSLog(@"n = %d, result = %d", n, sum(5, 6));

TestObject *obj = [[TestObject alloc] init];

obj.name = @"test out block";

NSString* (^Name)(TestObject *obj) = ^(TestObject *obj)
{
obj = [[TestObject alloc] init];

obj.name = @"test in block";

return obj.name;
};

NSLog(@"%@", obj.name);
NSLog(@"%@", Name(obj));
NSLog(@"%@", obj.name);
}
return 0;
}



我更改了指针指向,没有__block修饰,同样可以正常运行,不过运行结果不同:


2015-04-29 08:29:23.726 blocktest[10855:303] test out block
2015-04-29 08:29:23.726 blocktest[10855:303] test in block
2015-04-29 08:29:23.727 blocktest[10855:303] test out block
Program ended with exit code: 0

由于block里面重新申请了内存,所以此时已经不指向同一个地址了,当然无法改变外部obj.name的值。


总的说明一点儿,__block修饰符只是针对基本数据类型,对于对象类型就无法适用!


结尾补充一张块的表示图,简单理解块就是C语言中的回调函数。


__block修饰符的作用域问题