Block介绍(二)内存管理与其他特性

本文详细探讨了Block在不同情景下的存储位置,包括栈、堆和全局区,并解释了Block如何引用变量及其复制行为,特别关注Block与对象实例间的引用关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[+]



一、block放在哪里

我们针对不同情况来讨论block的存放位置:

1.栈和堆

以下情况中的block位于堆中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

void foo()

{

    __blockint i = 1024;

    intj = 1;

    void(^blk)(void);

    void(^blkInHeap)(void);

    blk = ^{ printf("%d, %d\n", i, j);};//blk在栈里

    blkInHeap = Block_copy(blk);//blkInHeap在堆里

}

 

- (void)fooBar

{

    _oi =1;

    OBJ1* oj =self;

    void(^oblk)(void) = ^{ printf("%d\n", oj.oi);};

    void(^oblkInHeap)(void) = [oblkcopy];//oblkInHeap在堆中

}

 

2.全局区

以下情况中的block位于全局区: 

1

2

3

4

5

6

7

8

9

10

static int(^maxIntBlock)(int,int) = ^(inta, int b){return a>b?a:b;};

- (void)fooBar

{

     int(^maxIntBlockCopied)(int,int) =[maxIntBlockcopy];

}

 

void foo()

{

     int(^maxIntBlockCopied)(int,int) = Block_copy(maxIntBlock);

}

需要注意的是,这里复制过后的block依旧位于全局区,实际上,复制操作是直接返回了原block对象。 

 

二、block引用的变量在哪里

 1.全局区

全局区的变量存储位置与block无关:

1

2

3

4

5

6

7

8

static int gVar = 0;

//__block static int gMVar = 1;

 

void foo()

{

    staticint stackVar = 0;

//    __block static int stackMVar = 0;

}

注意,static变量是不允许添加__block标记的

 

2.堆栈 


此时,你可能会问,当函数foo返回后,栈上的j已经回收,那么blkInHeap怎么能继续使用它?这是因为没有__block标记的变量,会被当做实参传入block的底层实现函数中,当block中的代码被执行时,j已经不是原来的j了,所谓物是人非就是这样吧~

另外,如果使用到变量j的所有block都没有被复制至heap,那么这个变量j也不会被复制至heap

因此,即使将j++这一句放到blk()这句之前,这段代码执行后,控制台打印结果也是:1024, 1。而不是1024, 2

 

 

三、其他特性

1.复制的行为

 block调用复制,有以下几种情况:

1.对全局区的block调用copy,会返回原指针,并且这期间不处理任何东西(至少目前的内部实现是这样);

2.对栈上的block调用copy,每次会返回新复制到堆上的block的指针,同时,所有__block变量都会被复制至堆一份(多次拷贝,只会生成一份)

3.对已经位于heap上的block,再次调用copy,只会增加block的引用计数。

为什么我们不讨论retian的行为?原因是并没有Block_retain()这样的函数,而且objc里面的retain消息发送给block对象后,其内部实现是什么都不做。 

2.objc类中的block复制

 objc类实例方法中的block如果被复制至heap,那么当前实例会被增加引用计数,当这个block被释放时,此实例会被减少引用计数。

但如果这个block没有使用当前实例的任何成员,那么当前实例不会被增加引用计数。这也是很自然的道理,我既然没有用到这个instance的任何东西,那么我干嘛要retian它?

我们要注意的一点是,我看到网上有很多人说block引起了实例与block之间的循环引用(retain-cycle),并且给出解决方案:不直接使用self而先将self赋值给一个临时变量,然后再使用这个临时变量。

但是,大家注意,我们一定要为这个临时变量增加__block标记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值