block 解析 - 内存

回顾

  1. 上一篇 我们了解到了用__block修饰的变量,可以在block内部修改,__block变量其实对应一个结构体

    struct __Block_byref__para1_0 {
      void *__isa;
    __Block_byref__para1_0 *__forwarding;
     int __flags;
     int __size;
     char *_para1;
    };

    block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了

    struct __main1_block_impl_0 {
      struct __block_impl impl;
      struct __main1_block_desc_0* Desc;
      __Block_byref__para1_0 *_para1; // by ref
    }
  2. 上上上一篇 介绍了block内部 成员变量 使用,可以在block内部修改,对应一个、多个成员变量参数,block结构体内部有一个成员引用

    struct __KDBlockTest__test3_block_impl_0 {
      struct __block_impl impl;
      struct __KDBlockTest__test3_block_desc_0* Desc;
      KDBlockTest *self;}

    这样会增加对self的引用,在Block销毁的时候引用就释放掉了

循环引用

在使用的时候需要注意循环引用,即某个对象有一个copy或者strong的 block成员属性,这时block内部直接引用了 成员变量(全局变量) 或者self,这样就产生了self持有 block成员,block成员又持有了self,就会导致循环引用。

我们看以下代码(ARC):

typedef void(^ActionTest)(void);
@interface KDBlockTest()
{
    __block NSString *_person2;
    ActionTest _action;
}
@implementation KDBlockTest
#pragma mark - system
-(instancetype)init
{
    self=[super init];
    if(self)
    {
        [self test3];
    }
    return self;
}
-(void)dealloc
{
    NSLog(@"KDBlockTest dealloc");
}

#pragma mark - private
////循环引用
-(void )test3
{
    _person2=@"person2";
    _action= ^(void) {
        //block内赋值
        NSLog(@"excuteing _person2:%@,%p",_person2,_person2);
    };
    _action();
}

这样我们执行以下代码:

KDBlockTest *_test=[[KDBlockTest alloc]init];

通过调试发现没有走到dealloc,这里不管成员变量 _person2 是否声明 __block都没有什么影响。

成员变量 这一篇已经详细介绍了,对于block 使用 成员变量、self来说,block内部是直接强引用self的。也就是block持有了self,在这里bock又作为self的一个成员被持有,所以就形成了相互引用,导致无法释放。

__weak

对于上面这种情况,我们引入了__weak解决,__weak不会增加对象的引用计数,而且当指向的内存销毁后,__weak指针会自动置为nil。

我们对上面的代码稍作修改

-(void )test3
{
    __weak typeof(self) _weakSelf=self;
    _person2=@"person2";
    NSLog(@"init:%@,%p,%p",_person2,_person2,&self);
    _action= ^(void) {
        //block内赋值
        NSLog(@"excuteing _person2:%@,%p,%p",_weakSelf.person2,_weakSelf.person2,&_weakSelf);
    };
    _action();
}

输出日志:

2014-07-29 13:38:30.872 Test[2642:60b] init:person2,0x5b980,0x27dae944
2014-07-29 13:38:30.875 Test[2642:60b] excuteing _person2:person2,0x5b980,0x1562ed44
2014-07-29 13:38:30.876 Test[2642:60b] KDBlockTest dealloc

从日志可以看出block内部使用 person2 、_weakSelf 和外面的 person2 、self 地址是一样的,看来也是引用关系,既达到block内部修改变量的效果,又没有对变量产生强引用。我们来看下转换后的代码:

block结构体的定义:

struct __KDBlockTest__test3_block_impl_0 {
  struct __block_impl impl;
  struct __KDBlockTest__test3_block_desc_0* Desc;
  __weak typeof (self) _weakSelf;
  __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self) __weakSelf, int flags=0) : _weakSelf(__weakSelf) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

重点就在这,使用_weak声明的self,block结构体对应 也生成了一个_weak的self成员。我们在看下 我们的test3 方法:

static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) {
    __attribute__((objc_gc(weak))) typeof(self) _weakSelf=self;
    (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_1;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_2,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&self);
    (*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakSelf, 570425344);
    ((void (*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)));
}

block初始化的时候把  _weakSelf的地址传入,block内部对_weakSelf进行弱引用。在执行block的时候

static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself) {
  __weak typeof (self) _weakSelf = __cself->_weakSelf; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_3,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),&_weakSelf);
    }

通过取得block结构体的 弱引用对象self 成员来访问相对应的方法 person2 (给对象发消息)。

__weak 成员变量

上面例子,我们稍作修改:

-(void )test3
{
    _person2=@"person2";
    __weak typeof(_person2) _weakPerson2=_person2;
    NSLog(@"init:%@,%p,%p",_person2,_person2,&_person2);
    NSLog(@"init weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);
    _action= ^(void) {
        //block内赋值    //_weakPerson2=@"person4";//error ,不能修改
        NSLog(@"excuteing _person2:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);
    };
    _person2=@"person22";
    NSLog(@"before:%@,%p,%p",_person2,_person2,&_person2);
    NSLog(@"before weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);
    _action();
    NSLog(@"after:%@,%p,%p",_person2,_person2,&_person2);
}

输出日志:

2014-07-29 15:29:33.472 Test[2719:60b] init:person2,0x5397c,0x16566db8
2014-07-29 15:29:33.475 Test[2719:60b] init weak:person2,0x5397c,0x27db693c
2014-07-29 15:29:33.476 Test[2719:60b] before:person22,0x539bc,0x16566db8
2014-07-29 15:29:33.477 Test[2719:60b] before weak:person2,0x5397c,0x27db693c
2014-07-29 15:29:33.479 Test[2719:60b] excuteing _person2:person2,0x5397c,0x165b5be4
2014-07-29 15:29:33.480 Test[2719:60b] after:person22,0x539bc,0x16566db8
2014-07-29 15:29:33.481 Test[2719:60b] KDBlockTest dealloc

从日志可以看出:

  1. 直接用__weak修饰符修饰_person2变量也可以,也可以避免循环引用,但是不可以在block内部修改外部 参数的值
  2. 在block外部修改变量指针指向,即把指针指向另外一块内存,block内部无法更新到。

我们来看下转换后的代码:其实和不加__block的局部变量差不多,无非多了一个弱引用,不会对引用计数有影响。

struct __KDBlockTest__test3_block_impl_0 {
  struct __block_impl impl;
  struct __KDBlockTest__test3_block_desc_0* Desc;
  __weak typeof (self->_person2) _weakPerson2;
  __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self->_person2) __weakPerson2, int flags=0) : _weakPerson2(__weakPerson2) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) {

    (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_1;//声明_weak 变量
    __attribute__((objc_gc(weak))) typeof(_person2) _weakPerson2=(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_2,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_3,_weakPerson2,_weakPerson2,&_weakPerson2);//初始化block
    (*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakPerson2, 570425344);
    (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_5;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_6,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_7,_weakPerson2,_weakPerson2,&_weakPerson2);
    ((void (*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)));
}

在声明 _weak变量的时候,生成了一个 弱引用的指针 指向 self的person2变量。在block初始化的时候,把弱引用指针指向的内容地址 传递给了block成员

__weak typeof (self->_person2) _weakPerson2;

block结构体内部通过 成员 _weakPerson2 直接弱引用了外部变量 person2的内容地址。这时候如果把person2指针指向另外一块内存地址,那么肯定是同步不到block内部的,这个和 局部变量  大同小异。

总结:

  1. 声明 __weak typeof(self) _weakSelf=self;  这样block内部 生成一个成员 ,会对self弱引用,对于值类型、引用类型都可以修改,并且修改指针指向都可以同步到任何地方。
  2. 声明 __weak typeof(_person2) _weakPerson2=_person2;  针对某个具体的成员变量使用weak修饰符,这样可以避免循环引用,并且不能再block内部修改_weakPerson2。规则如下:
    • 对值类型的修改,如果block初始化后,对值类型修改,无法同步到block内部。
    • 对于引用类型的修改,如果block初始化后,修改指针指向,即指向另外一块内存,这样也是无法同步到block内部
    • 对于引用类型的修改,如果block初始化后,对指针指向的内存进行修改,即NSMutableArray add 、remove操作,这样是可以用同步到block内部。

block 解析 - 内存

时间: 2024-07-30 10:13:40

block 解析 - 内存的相关文章

block 解析 - block变量

block变量 上一篇 讲的是block静态变量的特性,这里我们来看一下_block变量.引用官方: You can specify that an imported variable be mutable—that is, read-write— by applying the __block storage type modifier. __blockstorage is similar to, but mutually exclusive of, the register, auto, a

block 解析 - 局部变量

局部变量 block内使用局部变量,一般都是截获变量(只读),截获离block初始化最近的一次的值. 引用官方文档: Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.Their values are taken at the point of the block expression within the program. In nested blo

block 解析(2)

上一篇讲的是block和截获变量的特性,这里我们来看一下_block变量.引用官方:You can specify that an imported variable be mutable—that is, read-write— by applying the __block storage type modifier. __blockstorage is similar to, but mutually exclusive of, the register, auto, and static

block 解析 - 形参变量

block形参 之前漏了一篇block形参的介绍,这里给补上. block形参就是定义block带的参数,和函数的参数使用一样,我们可以在block随意使用修改block形参. 我们来看个例子: 我们声明了两个NSString 指针_p1 _p2.int 型_p3.可变数组_p4,并把这些参数传入block,在block内修改. -(void )test3 { NSString *_p1=[NSString stringWithFormat:@"hello %@",@"wor

block 解析 - 静态变量

静态变量 上一篇 我们了解了block全局变量的使用,静态变量和全局变量一样,可以直接在block内部使用,也可以在block内部修改 引用官方文档: Global variables are accessible, including static variables that exist within the enclosing lexical scope. 我们来看一段代码: 声明一个静态变量,在block内部修改 static NSString * _para1; -(void )tes

block 解析 - 成员变量

成员变量 在 上一篇 中我们讲了截获变量特性,对于局部变量,变量不加__block修饰符,在block内部是无法修改变量的值,而且初始化block之后,对变量修改,就无法同步到block内部,但是对于成员变量,结果却不一样,即时不加__block修饰符,block初始化后,对于block内部引用的变量的修改,也能同步到block内部,并且在block内部可以修改成员变量的值. Demo: 声明两个变量:_person2._p1 @interface KDBlockTest() { NSStrin

iOS Block的内存管理

1. Block实际上是指向结构体的指针,编译器会将内部的代码生成对应的函数 2. Block内部使用了外部的变量, 内部是否能修改外部的变量,取决于外部变量是怎么给Block传值的:  1>如果传的指针,就可以改; 2>如果传的只是变量的数值,则不可以改. 3. Block的内存管理:  1>默认情况下,Block的内存在栈中,它不会对所引用的对象做任何操作 Person *p = [[Person alloc]init]; p.age = 20; void (^block)() =

聊聊Block的内存管理那些事

对于大多数iOS开发人员来说,Block应该不算陌生,iOS4.0系统已开始支持Block,在编程过程中,Block被Objective-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行. Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值.它是对C语言的扩展,用来实现匿名函数的特性. Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外.词法作用域以内的外部变量的值. 换句话说,Block不仅 实现函数的功能,还能携带函数的执行环

Block在内存中的位置在arc和mrc的区别

关于block在内存中的位置, http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/这篇文章解释的不错,但是好像并没有区分arc和mrc的区别 block的位置分为这几种 NSGlobalBlock:类似函数,位于text段: NSStackBlock:位于栈内存,函数返回后Block将无效: NSMallocBlock:位于堆内存. 不引用外部环境变量的block都属于NSGlobalBlock, NSStackBl