如何使用__block 与 __weak

2,216 阅读3分钟

在使用block时要注意两点:

  1. block默认会拷贝局部变量的值,即所谓的闭包。
  2. block被某对象强引用,此时在block中使用这个对象,默认block会强引用这个对象。这时就产生了循环引用问题。

1. block默认会拷贝局部变量的值

(1)block默认会拷贝局部变量的值,block中的局部变量不可修改。

eg:代码1

//代码1
- (void)testMethod {
    int anInteger = 42;
    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };
    
    anInteger = 84;
    testBlock();    //输出: Integer is: 42
}

在block外声明局部变量并赋值, 在声明block时会copy局部变量当前的值(在此例中是anInteger的值始终是42)且不可修改。 此时block中的anIntegerblock外的anInteger不是同一个值(他两个的内存地址是不一样的)。所以就出现了上面的情况,anInteger = 84只是修改了block外的局部变量,它不会影响block里面的anInteger。同时block中的anInteger是不能被修改的。

(2)当某局部变量使用 __block 进行修饰时,此局部变量在block中不会产生copy。

eg: 代码2

  //代码2
	__block int anInteger = 42;
	void (^testBlock)(void) = ^{
	   NSLog(@"Integer is: %i", anInteger);
	};
	anInteger = 84;
	testBlock();     
	//输出:  Integer is: 84
	
	
	//在block中也可以修改原来的值
	__block int anInteger = 42;
	void (^testBlock)(void) = ^{
	   NSLog(@"Integer is: %i", anInteger);
	   anInteger = 100;
	};
	testBlock();
	NSLog(@"Value of original variable is now: %i", anInteger);
	
	//输出:
	Integer is: 42
	Value of original variable is now: 100

(3)当局部变量是指针类型时,你可以在block中使用这个局部变量,并修改指针所指的对象的内容。

eg:代码3

//代码3
	NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"abc",nil];
	NSMutableArray *mArrayCount = [NSMutableArray arrayWithCapacity:1];
	
	[mArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock: ^(id obj,NSUInteger idx, BOOL *stop){
	    [mArrayCount addObject:[NSNumber numberWithInt:[obj length]]];
	}];
	    
	NSLog(@"%@",mArrayCount);  //  1,1,3

代码3中与代码1的结论并不冲突。带代码3中 block中的mArrayCount确实是copy来的(与block外的mArrayCount在不同的内存空间,但是所指向的内存地址却是相同的。在block中改变的只是所指对象的内容。如果你在block中执行 mArrayCount=nil;,这才是修改局部变量的值。那么此时编译器会报错,因为违背了代码1.

2.使用__weak避免循环引用

如果一个对象强引用一个block, 然后在block中引用这个对象或者引用这个对象的属性, 此时block会强引用这个对象。 此时出现循环引用问题,造成内存泄露。__weak就是解决这个问题的。使用__weak 标记的引用是弱引用。此时在block中使用这个弱引用,block弱引用这个对象就不会产生循环引用的问题。 eg: 代码4

//代码4
@property (nonatomic,copy)void (^block)(void);
@property (nonatomic,copy)NSString *name;

- (void)testMethod {
    __weak typeof(self) weakSelf = self;
    _block = ^{
        weakSelf.name = @"hello world";  //此时不会产生循环引用问题
        NSLog(weakSelf.name);
    };
    _block();
}

此时有个问题没有解决.当在block 代码执行时self有可能会销毁成为nil, 如何解决确保block在执行时self不会被销毁那?那就使用代码5:

//代码5
 __weak typeof(self) weakSelf = self;
 _block = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf; //就这一句,搞定
   //这一句很重要,没有的话可能会崩溃的。
   if(strongSelf==nil){return;}
    strongSelf.name = @"hello world";
 };