Block用法总结(下)

本文详细介绍了Block在MRC和ARC下的内存管理,包括循环引用的问题及其解决办法。在MRC下,Block默认存储在栈中,需要通过copy操作移到堆中并手动管理内存,而ARC下Block自动在堆中管理,避免循环引用主要是通过__block关键字的使用。在循环引用场景中,使用下划线block修饰可以防止对象被意外retain,从而防止内存泄漏。
摘要由CSDN通过智能技术生成

一.Block在MRC下的内存管理:

void(^myBlock)() = ^{

NSLog(@"--");
};
myBlock();
  • 默认情况下,Block的内存存储在栈中,不需要开发人员对其进行内存管理
  • 当Block变量出了作用域,Block的内存会被自动释放

Person *p = [[Person alloc] init];

void(^myBlock)()=^{
NSLog(@"--%@--",p);
};

myBlock();

[p release];//Person对象在这里可以正常被释放.
  • 在Block的内存存储在栈中时,如果在Block中引用了外面的对象,不会对所引用的对象进行任何操作

void(^myBlock)() = ^{
NSLog(@"----");
};
myBlock();

Block_copy(myBlock);

//做一些事情

记得释放
Block_release(myBlock);

如果对Block进行一次copy操作,那么Block的内存会被移动到堆中,这时需要开发人员对其进行release操作来管理内存


Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"%@",p);
};
myBlock();
Block_copy(myBlock);

//做一些事情

Block_release(myBlock);

[p release];
Person对象在这里无法正常被释放,因为p在Block中被进行了一次retain操作
  • 如果对Block进行一次copy操作,那么Block的内存会被移动到堆中,在Block的内存存储在堆中时,如果在Block中引用了外面的对象,会对所引用的对象进行一次retain操作,即使在Block自身调用了release操作之后,Block也不会对所引用的对象进行一次release操作,这时会造成内存泄漏

__block Person *p = [[Person alloc] init];

void(^myBlock)() = ^{
NSLog(@"%@",p);
};
myBlock();
Block_copy(myBlock);

//做一些事情

Block_release(myBlock);

[p release]; Person对象在这里可以正常被释放
  • 1.如果对Block进行一次copy操作,那么Block的内存会被移动到堆中,在Block的内存存储在堆中时,如果在Block中引用了外面的对象,会对所引用的对象进行一次retain操作,为了不对所引用的对象进行一次retain操作,可以在对象的前面使用下划线下划线block来修饰


循环引用: 

  • 如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic,copy) void(^myBlock)();

@end

#import "Person.h"

@implementation Person

-(void)dealloc{
    NSLog(@"Person dealloc");
    Block_copy(_myBlock);
    [super dealloc];
    
}

@end


#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {


    Person *p = [[Person alloc] init];
    p.myBlock = ^{
        NSLog(@"%@",p);
    };
    p.myBlock();
    
    [p release];
    NSLog(@"%ld",[p retainCount]);


    return 0;
}

//结果
2018-07-29 22:18:17.530846+0800 blocks[11948:533963] <Person: 0x100609280>
2018-07-29 22:18:17.531194+0800 blocks[11948:533963] 1

情况2:

@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

- (void)resetBlock;

@end


@implementation Person

- (void)resetBlock
{
    self.myBlock = ^{
        NSLog(@"------%@", self);
    };
}

- (void)dealloc
{
    NSLog(@"Person dealloc");
    
    Block_release(_myBlock);
    
    [super dealloc];
}

@end



Person *p = [[Person alloc] init];
[p resetBlock];
[p release]; 
Person对象在这里无法正常释放,虽然表面看起来一个alloc对应一个release符合内存管理规则,但是实际在resetBlock方法实现中,Block内部对self进行了一次retain操作,导致循环引用无法释放

解决办法:

  • 如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用,解决循环引用的办法是在对象的前面使用下划线下划线block来修饰,以避免Block对对象进行retain操作
@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

@end


@implementation Person

- (void)dealloc
{
    NSLog(@"Person dealloc");
    
    Block_release(_myBlock);
    [super dealloc];
}

@end


__block Person *p = [[Person alloc] init];
        
p.myBlock = ^{
    NSLog(@"------%@", p);
};
p.myBlock();
        
[p release];  Person对象在这里可以正常被释放

    NSLog(@"%ld",[p retainCount]); 还是1
p = nil; 回收指针,因为指针还在指向僵尸对象.

情况二:

@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

- (void)resetBlock;

@end


@implementation Person

- (void)resetBlock
{
     这里为了通用一点,可以使用__block typeof(self) p = self;
    __block Person *p = self;
    self.myBlock = ^{
        NSLog(@"------%@", p);
    };
}

- (void)dealloc
{
    NSLog(@"Person dealloc");
    
    Block_release(_myBlock);
    
    [super dealloc];
}

@end


Person *p = [[Person alloc] init];
[p resetBlock];
[p release]; // Person对象在这里可以正常被释放


二.Block在ARC下的内存管理:

  • 在ARC默认情况下,Block的内存存储在堆中,ARC会自动进行内存管理,程序员只需要避免循环引用即可
 当Block变量出了作用域,Block的内存会被自动释放
void(^myBlock)() = ^{
    NSLog(@"------");
};
myBlock();

  • 在Block的内存存储在堆中时,如果在Block中引用了外面的对象,会对所引用的对象进行强引用,但是在Block被释放时会自动去掉对该对象的强引用,所以不会造成内存泄漏
Person *p = [[Person alloc] init];
        
void(^myBlock)() = ^{
    NSLog(@"------%@", p);
};
myBlock();
        
 Person对象在这里可以正常被释放

循环引用: 

  • 如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用
@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

@end


@implementation Person

- (void)dealloc
{
    NSLog(@"Person dealloc");
}

@end


Person *p = [[Person alloc] init];
        
p.myBlock = ^{
    NSLog(@"------%@", p);
};
p.myBlock();


因为myBlock作为Person的属性,采用copy修饰符修饰(这样才能保证Block在堆里面,以免Block在栈中被系统释放),
所以Block会对Person对象进行一次强引用,导致循环引用无法释放

@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

- (void)resetBlock;

@end


@implementation Person

- (void)resetBlock
{
    self.myBlock = ^{
        NSLog(@"------%@", self);
    };
}

- (void)dealloc
{
    NSLog(@"Person dealloc");
}

@end


Person *p = [[Person alloc] init];
[p resetBlock];

 Person对象在这里无法正常释放,在resetBlock方法实现中,Block内部对self进行了一次强引用,
导致循环引用无法释放



情况一:

@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

@end


@implementation Person

- (void)dealloc
{
    NSLog(@"Person dealloc");
}

@end


Person *p = [[Person alloc] init];
__weak typeof(p) weakP = p;

p.myBlock = ^{
    NSLog(@"------%@", weakP);
};
p.myBlock();
        
 Person对象在这里可以正常被释放

情况二:

@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

- (void)resetBlock;

@end


@implementation Person

- (void)resetBlock
{
    // 这里为了通用一点,可以使用__weak typeof(self) weakP = self;
    __weak Person *weakP = self;
    self.myBlock = ^{
        NSLog(@"------%@", weakP);
    };
}

- (void)dealloc
{
    NSLog(@"Person dealloc");
}

@end


Person *p = [[Person alloc] init];
[p resetBlock];

 Person对象在这里可以正常被释放

注: 关于下划线下划线block关键字在MRC和ARC下的不同

__block在MRC下有两个作用
1. 允许在Block中访问和修改局部变量 
2. 禁止Block对所引用的对象进行隐式retain操作

__block在ARC下只有一个作用
1. 允许在Block中访问和修改局部变量

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值