ios Block

1:捕获

2:block类型

2.1:问题 :mrc环境下  下面讲的都是mrc环境下,会真实很多

2.2:在arc下,block 自动加上copy的情况---:返回block。

2.3:在arc下,block 自动加上copy的情况---:强指针__block;

2.4:arc环境下:方法名中有usingBlock的方法参数时,也会进行copy操作。

2.5:在arc下,block 自动加上copy的情况---:block作为GCD的方法参数。

3:对象类型的auto变量

3.1:假设在mrc环境下

3.2:arc:对上面做一个原因解释

4:如何修改block内部的变量:__block、static、全局、指针

4.1:__block的内存管理

4.2:__forwarding指针原理

4.3:问题:blok内部会对下面两个对象有什么不同么

4.4:被__block修饰的对象类型:arc环境下

5:循环引用

下面以一个简单的例子开始讲起

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
     
        int age = 10;
        void (^ block)(void) = ^{
// age的值捕获进来,(capture)
            NSLog(@"block:%d",age);
        };
        age = 20;
        block();

        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

通过 (这里解释一下,编译如果xcode路径出问题可能导致编译失败,结果办法点击

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

把main.m -> main.cpp文件。

查看上面block的源码

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int age = 10;
        void (* block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
        age = 20;
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

整理一下就是

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
          
        int age = 10; // 自动变量auto,离开作用域便自动销毁 c语言特性

        // 定义block变量

        void (* block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
        // 上面相当于是
        void (* block)(void) = &__main_block_impl_0(
                                                    __main_block_func_0,
                                                    &__main_block_desc_0_DATA
                                                    , age);
        
        age = 20;
       // 执行block内部的代码
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        // 上面相当于是:
        block->FuncPtr(block);
        
        

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

__main_block_impl_0这个函数。


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
    // 构造函数(类似oc的init方法),返回结构体对象 这个也就是block指向的 c++的特性 传进来的_age会自动赋值给age
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// 封装了block执行逻辑的函数:block里面的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy 取出block里面的age

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_ff1ca7_mi_0,age);
        }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};



struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

1:捕获

捕获:block内部会专门新增一个成员来外面变量的值,这个操作称之为捕获。

auto只存在于局部变量里,不能存在于全局变量中,因为它出了作用域就会被销毁。值传递,相当于把10传递进去。(auto是自动销毁的,所以必须要传值。)

static,址传递。看下面

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        int age = 10;
        static int height = 10;
        void (^ block)(void) = ^{
            NSLog(@"block:%d  height:%d",age,height);
        };
        age = 20;
        height = 20;
        block();
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

所以上面的age捕获的是值,所以block内部的age跟外面的int age并不是一个,block里面的age是重新创建的,只是这里是把int age的值赋值给了block里面的age。只是因为这种情况下age无法在block里面进行赋值操作,所以age是不是改变的就不重要了但是也要注意,这种在block里面操作取值,长时间的操作,会有不准确性。(这种情况是局部变量age情况下)

上面的static height 跟block里面的*height是一个,因为__main_block_impl_0()这个函数传进去的是height指针,所以,这里block里面的*height修改了,同样外面的值也就修改了。

如果是全局变量


int age = 10;
static int height = 10;

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
       
        void (^ block)(void) = ^{
            NSLog(@"block:%d  height:%d",age,height);
        };
        age = 20;
        height = 20;
        block();
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

这个打印出来

2018-08-16 22:44:17.640 newxc[16609:5312874] block:20  height:20

源码

因为是全局变量,所以不论在哪里都能够访问,所以没有必要捕获。

(这种情况下,block内部的height和age,就是拿的全局变量的指针进行操作的,这本就是一个数据进行的操作。)

局部变量需要捕获,是因为局部变量的作用域只仅限在当前函数作用域,如果block函数和调用不在一个函数作用域,那么就会跨函数访问局部变量,所以需要捕获。

总结如下。

问:如果block内部调用self,会捕获么

#import "Person.h"

@implementation Person

- (void)test
{
    void (^ block)(void) = ^{
        NSLog(@"self:%p",self);
    };
    
    block();
    
}
@end

答案会捕获。

因为test在c语言中的写法是

参数就是局部变量,所以会被捕获。

person的test的c语言函数就是下面这样,Person * self, SEL _cmd这两个参数是默认必传的参数。

static void _I_Person_test(Person * self, SEL _cmd) {
    void (* block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

}

// 看这里还有load方法
static void _C_Person_load(Class self, SEL _cmd) {

}

看一下上面的源码

// 看这里,里面有一个Person *self 参数里的self就会自动赋值给*self。
struct __Person__test_block_impl_0 {
  struct __block_impl impl;
  struct __Person__test_block_desc_0* Desc;
  Person *self;
  __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// 这里是block内部函数,在访问self。
static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
  Person *self = __cself->self; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_Person_4d5a32_mi_0,self);
    }
static void __Person__test_block_copy_0(struct __Person__test_block_impl_0*dst, struct __Person__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __Person__test_block_dispose_0(struct __Person__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __Person__test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __Person__test_block_impl_0*, struct __Person__test_block_impl_0*);
  void (*dispose)(struct __Person__test_block_impl_0*);
} __Person__test_block_desc_0_DATA = { 0, sizeof(struct __Person__test_block_impl_0), __Person__test_block_copy_0, __Person__test_block_dispose_0};


// 这里是test函数 __Person__test_block_impl_0这个函数传的值是self,上面是__Person__test_block_impl_0函数可以看到函数内部创建了一个person,来保存这个self指针。
static void _I_Person_test(Person * self, SEL _cmd) {

    void (* block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));

// 调用block
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

}



struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

问题:如果是访问_name呢?


- (void)test
{
    void (^ block)(void) = ^{
        NSLog(@"self:%p",_name);
    };
    
    block();
    
}

答案是会捕获,这个相当于是self->_age; 这个self是在block的局部的,这个捕获的是self,然后访问的self里面的_age成员变量。并不是单独对_age进行捕获,而是对self进行捕获。看源码

问题:如果是[self name];

这个也是捕获的,因为先要拿到self,才能发消息

这里需要知道一个问题:self 是局部变量,不是全局变量!!!

2:block类型

从下面代码的isa可以看出block是有类型的

struct __Person__test_block_impl_0 {
  struct __block_impl impl;
  struct __Person__test_block_desc_0* Desc;
  Person *self;
  __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock; // block的类型
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

block就是一个oc对象。我们可以通过[block class] 来查看一下它的类型。

我们来打印一下 这里是arc环境下的,跟mrc还是不一样的

int main(int argc, char * argv[]) {
    @autoreleasepool {
        void (^ block)(void) = ^{
        };
        
        block();
        
        NSLog(@"%@", [block class]);
        NSLog(@"%@", [[block class] superclass]);
        NSLog(@"%@", [[[block class] superclass] superclass]);
        NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
    
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

结果 :印证了 block是一个oc对象,isa本质上来说是从NSObject对象中来的。

// __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject

2018-08-17 20:04:39.088 newxc[17439:5526253] __NSGlobalBlock__
2018-08-17 20:04:39.089 newxc[17439:5526253] __NSGlobalBlock
2018-08-17 20:04:39.090 newxc[17439:5526253] NSBlock
2018-08-17 20:04:39.090 newxc[17439:5526253] NSObject

block到底有哪几种呢

        // Global:没有访问auto变量。包括static 都是global
        void (^ block)(void) = ^{
            NSLog(@"self:");
        };
        
        // Stack:访问了auto变量。 :这个需要把arc关掉。
        int ne = 10;
        void (^ block1)(void) = ^{
            NSLog(@"self:%d",ne);
        };
        
        NSLog(@"%@ %@ %@",[block class], [block1 class],[^{
            NSLog(@"%d",ne);
        } class]);
        
        block();
2018-08-17 20:12:12.046 newxc[17492:5539840] __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__

但是通过编译到.cpp文件中查看跟上面的类型不一致。isa都是_NSConcreteStackBlock类型

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

struct __main_block_impl_1 {
  struct __block_impl impl;
  struct __main_block_desc_1* Desc;
  int ne;
  __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _ne, int flags=0) : ne(_ne) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

struct __main_block_impl_2 {
  struct __block_impl impl;
  struct __main_block_desc_2* Desc;
  int ne;
  __main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, int _ne, int flags=0) : ne(_ne) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

不一致原因:

一切以运行时结果为准。

有时候通过clang 转成的c++代码,有时候并不是真正转成的c++代码。(苹果的llvm1.0不再是生成c++文件,而是生成中间代码,但是大致上相同,只有少数不同。llvm编译器的一部分就是clang,就是clang属于llvm编译器里面的一部分)从上到下是高地址到低地址。

2.1:问题 :mrc环境下  下面讲的都是mrc环境下,会真实很多

先看一下block的类型

 

看下面的问题

void (^block)(void);

void test() {
    // stack
    int age = 10;
    block = ^ {
        NSLog(@"block------%d",age);
    };
}


int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        test();
        block();
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

显然:这个block的结果会有问题

2018-08-18 20:50:15.110 newxc[18572:6009580] block------272632489

那为什么会这样呢? 是因为这个block函数在栈上,block在全局变量区,但是这个block代码块内的所有包括这个age都是在栈上,栈有一个特点就是随时可能就会被释放,所以,这个打印的结果是不确定的。那如何解决呢?把这个block放在堆里。也即是把这个block变成malloc类型。调用copy。下面这个就是在堆上的就是block。

void test() {
    // stack
    int age = 10;
    block = [^{
        NSLog(@"block------%d",age);
    } copy];
    
}

那如果是global类型调用copy呢 是什么类型呢

      block = [^{
            //NSLog(@"block------%d",age);
        } copy];
        
        block();
        NSLog(@"block:%@",[block class]);

答案还是global类型

2018-08-18 21:04:59.322 newxc[18618:6028439] block:__NSGlobalBlock__

那上面stack调用两次copy呢?(调用一次在堆上,再调用一次呢)

 block = [[^{
        NSLog(@"block------%d",age);
    } copy] copy];

答案还是malloc:也即是malloc调用copy还是在堆区,但是并不会再开辟内存了,而是引用计数器+1,但是这种的记住要自己管理内存。

2018-08-18 21:07:08.597 newxc[18641:6032580] block:__NSMallocBlock__

2.2:在arc下,block 自动加上copy的情况---:返回block。


typedef void (^block)(void);

block test() {
    // stack
    int age = 19;
    return ^{
        NSLog(@"block------%d",age);
    };  
}

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        block bloc = test();
        bloc();
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

因为test函数内return的block是栈上的block,一旦函数作用域消失,那这个block就消失了,所以不是很妥当。如果是arc环境下,会自动给返回的这个block加了一个copy操作。在你函数作用域消失的时候,会自动给你加上release操作。

2.3:在arc下,block 自动加上copy的情况---:强指针__block;

在arc环境下,只要被强指针持有,就是malloc。就会自动给你这个block加copy。

        int age = 10;
        
        block bloccc = ^{
            NSLog(@"---%d",age);
        };
        NSLog(@"%@",[bloccc class]);
2018-08-19 17:10:03.248 newxc[19403:6283487] __NSMallocBlock__

但是在mrc环境下 就是

2018-08-19 17:12:09.420 newxc[19450:6287484] __NSStackBlock__

2.4:arc环境下:方法名中有usingBlock的方法参数时,也会进行copy操作。

NSArray *array = @[];
        [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
        }];

有usingBlock 这个“^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { }”block也会搬到堆上面去。

2.5:在arc下,block 自动加上copy的情况---:block作为GCD的方法参数。

  static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
            
        });

只要是CGD里面的block,“ ^{ }”这个block就会自动添加copy操作。

so:小总结

在mrc下一定要用copy,在arc下可以用strong也可以用copy,用strong是因为有强指针,就会被自动添加copy,但是为了项目在哪里都能用,所以建议用copy。

 

3:对象类型的auto变量

arc环境

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        Person *person = [[Person alloc] init];
        person.age = 10;
        
        block bloccc = ^{
            NSLog(@"---%d",person.age);
        };
        NSLog(@"-----=====");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

源码是

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

这里是auto变量是什么捕捉的就是什么。如果是 static Person *person; 那么源码中捕捉到的就是Person **person.

因为整个block在堆上,而内部是对person的指针指引,所以只要block不释放,对person的指针就不会释放,person也就不会被释放。

3.1:假设在mrc环境下

int main(int argc, char * argv[]) {
    @autoreleasepool {
        block blo;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            
            blo = ^{
                NSLog(@"---%d",person.age);
            };            
            [person release];
        }
        NSLog(@"========");
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}


#import "Person.h"
@implementation Person

- (void)dealloc
{
    [super dealloc];
    NSLog(@"person-dealloc----");
}
@end
2018-08-19 17:58:05.680 newxc[19773:6348592] person-dealloc----
2018-08-19 17:58:05.681 newxc[19773:6348592] ========

为啥呢:因为这个block是在栈上,而且栈空间上的是不会持有对象的。如果是堆空间,是有能力保住这个对象的。所以person就dealloc了。

如果对这个block copy 那么person就不会dealloc了,因为block相当于对person做了retain操作。

假设在arc环境下----------这里2.1.1会做详细解释,从这里做案例开始.

nt main(int argc, char * argv[]) {
    @autoreleasepool {
        block blo;
        
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            
            blo = ^{
                NSLog(@"---%d",person.age);
            };
        }
        
        NSLog(@"========");
        
        
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

}
2018-08-19 18:12:54.016 newxc[19963:6373020] ========
2018-08-19 18:12:54.017 newxc[19963:6373020] person-dealloc----

会先出了作用域,block释放,然后block对其person的指针也没了,所以person释放。

假设用__weak来修饰person实例

int main(int argc, char * argv[]) {
    @autoreleasepool {
        block blo;
        
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            
            __weak Person *wekaPerson = person;
            blo = ^{
                NSLog(@"---%d",wekaPerson.age);
            };
        }
        
        NSLog(@"========");
        
        
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

}
2018-08-19 18:15:29.656 newxc[19986:6377817] person-dealloc----
2018-08-19 18:15:29.660 newxc[19986:6377817] ========

看结果不一样了,为啥呢,

3.2:arc:对上面做一个原因解释

弱引用:需要运行时的支持的。

            Person *person = [[Person alloc] init];
            person.age = 10;
            
            __weak Person *wekaPerson = person;
            blo = ^{
                NSLog(@"---%d",wekaPerson.age);
            };

你会发现,当你写了__weak,在去调用下面的命令时会报错

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
/var/folders/27/tr3mg74j3nq_zxgdyg1rj6480000gn/T/main-6a8820.mi:47464:28: error: 
      cannot create __weak reference because the current deployment target does
      not support weak references
            __attribute__((objc_ownership(weak))) Person *wekaPerson = person;
                           ^
1 error generated.

你会发现上述报错,只是静态的编译会报错,所以需要我们指定是runtime并且指定arc环境。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

发现,不一样的地方,就是person弱引用了

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__weak wekaPerson;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _wekaPerson, int flags=0) : wekaPerson(_wekaPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

那如果是不加__weak呢 ,这里注意要接着用上面的指令编译,发现结果,是strong.

typedef void (*Block)(void);

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__strong per;  //注意这里,是strong类型,这是没有加__weak的情况,同样也没有加__strong
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

那如果是加了__strong的情况呢?接着使用上指令 结果可以看到依然是__strong修饰

          Block blo;
        {
          
            __strong Person *per = [[Person alloc] init];
            per.age = 100;
            
            blo = ^{
                NSLog(@"---%d",per.age);
            };
        };

typedef void (*Block)(void);


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__strong per;  // 主要看这里,是用__strong来修饰的
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  Person *__strong per = __cself->per; // bound by copy

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_a9de8d_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)per, sel_registerName("age")));
            }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

好总结一下,

如果block在栈上(不论是arc还是mrc),都不会对block内部的产生强引用。(因为自己都是栈上的,随时会被销毁,所以不会对里面的对象产生强引用。)

可以看到这个有对象的block跟以前的block不太一样。不太理解不一样的,可以看1.1捕获上面的源码跟这个的区别,是多了两个函数,请看下面。

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}

同样可以看到这个函数也有赋值

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

__main_block_copy_0赋值给copy函数,__main_block_dispose_0赋值给dispose函数。

也就是说,上面的blo一旦进行copy操作,要copy到堆上,就会自动调用copy函数,这个函数会调用里面的_Block_object_assign函数,这个函数做的事情是:会根据外部auto变量传进来的是strong还是weak,来对__main_block_impl_0里面的Person指针进行对外部的person对象是strong还是weak。如果是传进来的是strong,则__main_block_impl_0里面的person指针会强引用着外部的person对象,如果是weak也是一样。

那dispose什么时候调用呢? 当堆上的blo 进行销毁时,就会自动调用block内部的dispose,就会释放引用的那些函数

看总结图

看题

苹果会认为block内部有强引用的时候,不会释放,等到强引用释放完了才会释放。弱引用没有影响。

额外提示一下,什么情况下才会出现copy和dispose这两个函数呢?

一旦block内部访问的是对象类型的,就会出现copy和dispose函数,因为访问的是对象,想拥有这个对象,所以就会有内存对象管理。

4:如何修改block内部的变量:__block、static、全局、指针

如果我们想对block里面的变量进行修改,怎么做?

a:static

b:弄成全局变量  

c:用__block.

d:数据类型用指针指向(参考static使用的block内部原理)

1:先看d也就是static使用的block的内部原理

typedef void(^Block)(void);

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

        static int height = 10;

        Block bl = ^{
            height = 199;
        };
        bl();
    }
    return 0;
}
typedef void(*Block)(void);


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *height;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_height, int *__newhei, int flags=0) : height(_height){
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *height = __cself->height; // bound by copy
            (*height) = 199;

        }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {

        static int height = 10;

        Block bl = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &height));

        ((void (*)(__block_impl *))((__block_impl *)bl)->FuncPtr)((__block_impl *)bl);

    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

从上面可以看出来 传进去的是height的指针,从__main_block_impl_0对象结构体中也可以知道,是拿int *height;来接收的,在__main_block_func_0函数中可以看到赋值时候用的是int *_newhei = __cself->_newhei;   *_newhei = 20; .所以我们可以这么做


typedef void(^Block)(void);

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

        int age = 10;
        
        int *_hei = &age;

        int *_newhei = _hei;

        *_newhei = 304;

        Block bl = ^{
            *_newhei = 20;

        };
        bl();
        NSLog(@"在这里打印的话会发现age已经变为20 了 并且*_newhei 也是20")
        
    }
    return 0;
}

当然 这种方式是麻烦了点,而且只针对基本数据类型,对于对象类型不适用。那为什么不适用呢,请看下面


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

        NSInteger df = 19;

        static int lage = 100;

        Block bl = ^{

            lage = 300;
            NSLog(@"%@",per.name);

        };
        bl();

这个底层源码是 我们只看这一段

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *lage = __cself->lage; // bound by copy
  Person *per = __cself->per; // bound by copy


            (*lage) = 300;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_70_0rj_h7dx3zv20xx9ln3x0d8h0000gn_T_main_3efd6b_mi_0,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)per, sel_registerName("name")));

        }

注意到,对于基本数据类型,这里取值都用的是*lage 而不是lage,也就是这是取值,并不是取的指针。

但是对于对象来说,取的是per,也就是(id)per,而不是*per,(这个*per在oc中也不代表什么)。所以对于oc对象来说,同样跟c语言一样有指针,但是方式却不一样。 所以oc对象来说 应该方式都是这样的下面包装一层的方式

2:看一个例子 用__block

typedef void (^Block)(void);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        __block int age = 10;
            
        Block blo = ^{
            
                NSLog(@"---%d",age);
            };
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

typedef void (*Block)(void);

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding; // 看下面的引用知道,这个指向自己
 int __flags;
 int __size;
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 __Block_byref_age_0 *age = __cself->age; // bound by ref

            (age->__forwarding->age) = 20;

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_0dc06e_mi_0,(age->__forwarding->age));
            }
// 要对__main_block_impl_0对象进行内存管理,所有有copy和dispose
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

// 这里是 __block int age = 10; 是下面的这个
       
 __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
(void*)0,
(__Block_byref_age_0 *)&age, 
0, 
sizeof(__Block_byref_age_0), 
10
};

        Block blo = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

    }
    return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));

}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

// 要对__Block_byref_age_0对象进行内存管理,所有有copy和dispose

看这个结构体 这个是调用age,能看出来第二个(__Block_byref_age_0 *)&age赋值给了__Block_byref_age_0结构体的forwarding指针,也就是把自己的地址赋值给了自己结构体中的forwarding。

__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {

(void*)0,

(__Block_byref_age_0 *)&age, 

0, 

sizeof(__Block_byref_age_0),

10};  
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

接着看这个,看blo这个结构体把age的地址:&age 传进去了,赋值给了第三个 __Block_byref_age_0 *age; // by ref

Block blo = (
(void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
&__main_block_desc_0_DATA, 
(__Block_byref_age_0 *)&age, 
570425344));
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

所以,block内部会有一个指针(这里是*age)指向__Block_byref_age_0结构体,这个结构体内部又有age这个值,存储这个值,那是如何修改这个值的呢?

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref

            (age->__forwarding->age) = 20;
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_acce32_mi_0,(age->__forwarding->age));
            }

看这个__cself从__main_block_impl_0这个结构体中拿到age也就是__Block_byref_age_0结构体,然后用这个结构体先访问自己内部的forwarding,因为forwarding指向的就是自己,所以再拿age,再赋值,就赋值成功啦。那到此肯定有疑问,这个改变的不就是block内部的age这个值么,而且这个age是在内部创建的,验证。打印一下

   __block int age = 10;
        Block blo = ^{
            age = 20;
            NSLog(@"---%d",age);
        };
        blo();

        NSLog(@"外面:%d",age);
2018-09-07 22:01:44.772 newxc[31713:10543714] ---20
2018-09-07 22:02:16.602 newxc[31713:10543714] 外面:20

那是为啥呢?看这个,不加block,只用__block修饰,再看源码

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

        __block int age = 10;

        NSLog(@"外面:%d",age);
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_80e809_mi_0,(age.__forwarding->age));

    }
    return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));

}

请看,有__Block_byref_age_0结构体,就是说只要加上__block,就会被包装秤这个对象结构体.在nslog中访问的就是__Block_byref_age_0结构体中的age,所以是能修改的,这个原来船进入的是10,并不是10的地址,而是这个结构体中的age是新生成的age变量,这只是造成了一个假象,显示这个age被修改了,其实是修改的这个__Block_byref_age_0结构体对象里面的age变量,验证如下 (就如同,外面是一个person变量,有一个age的属性,在block内部给age赋值,是修改成功的)。


当使用了__block之后,age的地址就发生了变化,这里因为arc环境下,block已经copy到堆上,但是原来__block修饰的age的那个对象还在栈上,而最后age= 20,这个age,是访问的堆上的age,(因为已经从栈到堆了。)

总结

那如果再加上一个person对象呢用__block修饰?

typedef void(^Block)(void);

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

        __block Person *per = [[Person alloc] init];
        __block int age = 10;

        Block bl = ^{

           NSString *a =  per.name;
            per.name = @"34";
            age = 300;
        };
        bl();
    }
    return 0;
}
typedef void(*Block)(void);

struct __Block_byref_per_0 {
  void *__isa;
__Block_byref_per_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *per;
};
struct __Block_byref_age_1 {
  void *__isa;
__Block_byref_age_1 *__forwarding;
 int __flags;
 int __size;
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_per_0 *per; // by ref
  __Block_byref_age_1 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_per_0 *_per, __Block_byref_age_1 *_age, int flags=0) : per(_per->__forwarding), age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_per_0 *per = __cself->per; // bound by ref
  __Block_byref_age_1 *age = __cself->age; // bound by ref


           NSString *a = ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(per->__forwarding->per), sel_registerName("name"));
            ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(per->__forwarding->per), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_70_0rj_h7dx3zv20xx9ln3x0d8h0000gn_T_main_b0ec3f_mi_0);
            (age->__forwarding->age) = 300;
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_per_0 per = {(void*)0,(__Block_byref_per_0 *)&per, 33554432, sizeof(__Block_byref_per_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"))};
        __attribute__((__blocks__(byref))) __Block_byref_age_1 age = {(void*)0,(__Block_byref_age_1 *)&age, 0, sizeof(__Block_byref_age_1), 10};

        Block bl = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_per_0 *)&per, (__Block_byref_age_1 *)&age, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)bl)->FuncPtr)((__block_impl *)bl);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

看上面就会生成两个__main_block_impl_结构体,并且最后nslog里,访问的也是__main_block_impl_结构体里面的forwarding里的per或者age。

4.1:__block的内存管理

(如果是对象修饰用__block的话,记住两个copy是不同的,__main_block_copy_0和__Block_byref_id_object_copy。作用对象也不同)

强引用__Block_byref_age_0 *age; 对象,对这个强引用(copy)

销毁对__Block_byref_age_0 *age;对象进行release。

(这个内存管理对没有修饰的对象类型的copy是一样的,如果加上了__block, 那么就是再加上一层内存管理,也就是__block变量内部对对象类型的内存管理。)

4.2:__forwarding指针原理

那上面间接的可以解释为什么有__forwarding指针,而不是age直接指向age

(age->__forwarding->age) = 20;

因为这个__Block_byref_age_0对象刚开始都是在栈上,当block被copy到堆上的时候,自动也就copy了一份__Block_byref_age_0对象,这个时候,第一个age是栈上的,而它的__forwarding指针是指向堆上的它自己,所以需要用__forwarding的age来取值或者赋值,但是如果block是在栈上,那么这些都不会被copy到堆上,所以__forwarding还是指向自己,所以,这不论是在栈还是再堆都实现了指向自己,所以可以赋值或者取值。(copy到堆上了,地址肯定就发生变化了。)

4.3:问题:blok内部会对下面两个对象有什么不同么

Person *per = [[Person alloc] init];
        __block int age = 10;

不同点在这里

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}

一旦用__block,这个assign函数就会对这个对象产生强引用,而没有用__block的对象,会根据是strong还是weak来修饰的,再对它产生强或者弱引用。

那相同点是什么呢?

4.4:被__block修饰的对象类型:arc环境下

这里着重看一下下面两个

        __block  Person *weakPerson = per;
        __block  __weak Person *weakPerson = per;

这两个在代码上有需要注意的地方,先看下__block  Person *weakPerson = per;的源码

typedef void (^Block)(void);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        
        Person *per = [[Person alloc] init];
        
        __block  Person *weakPerson = per;
        Block blok = ^{
            weakPerson.age = 20;
        };
        blok();
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

}

#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}   // 看这里assign 对 person这个对象进行copy,也就是进行131,根据外面传进来的操作。

static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}


typedef void (*Block)(void);

struct __Block_byref_weakPerson_0 {
  void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__strong weakPerson;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_weakPerson_0 *weakPerson; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_weakPerson_0 *weakPerson = __cself->weakPerson; // bound by ref

            ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)(weakPerson->__forwarding->weakPerson), sel_registerName("setAge:"), 20);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        Person *per = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));

        __attribute__((__blocks__(byref))) __Block_byref_weakPerson_0 weakPerson = {
(void*)0,
(__Block_byref_weakPerson_0 *)&weakPerson, 
33554432, 
sizeof(__Block_byref_weakPerson_0), 
__Block_byref_id_object_copy_131, 
__Block_byref_id_object_dispose_131, 
per};   // 这里的封装好的weakPerson放在下面里面.

        Block blok = (
(void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
&__main_block_desc_0_DATA, 
(__Block_byref_weakPerson_0 *)&weakPerson, 
570425344));

        ((void (*)(__block_impl *))((__block_impl *)blok)->FuncPtr)((__block_impl *)blok);

    }
    return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));

}

看这个用__block修饰的对象跟__block修饰的普通数据类型不一样的地方,在__Block_byref_weakPerson_0对象里多了两个参数, void (*__Block_byref_id_object_copy)(void*, void*);和 void (*__Block_byref_id_object_dispose)(void*);那这两个的作用是什么呢?就是管理__Block_byref_per_0中_Person *__strong weakPerson;的内存的,也就是是对象就需要的内存管理。

接着分析这个过程,刚开始把weakPerson封装好给blok赋值,从__Block_byref_weakPerson_0结构中可以看到,赋值的内容:isa是指针,

__forwarding是赋值的地址&weakPerson,

__flags赋值的是33554432,

__size赋值的是sizeof(__Block_byref_weakPerson_0),

__Block_byref_id_object_copy赋值的是 __Block_byref_id_object_copy_131,

__Block_byref_id_object_dispose赋值的是__Block_byref_id_object_dispose_131,

Person *__strong weakPerson;赋值的是per。

所以__forwarding指向的就是自己。而__Block_byref_id_object_copy_131看这个函数内是这个_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131); 这个是这个结构体指针的地址加40,也就是__Block_byref_weakPerson_0的地址加40 就是 Person *__strong weakPerson;,对它进行的copy和dispose操作。

所以 过程是:

当block被copy到堆上的时候,(arc环境下),block会调用__main_block_copy_0函数,来对__main_block_impl_0对象内部的 __Block_byref_weakPerson_0 *weakPerson;进行copy,进行强指针引用。同时__Block_byref_weakPerson_0对象也被拷贝到堆上,在这个copy的时候,__Block_byref_weakPerson_0对象就会调用自己内部的__Block_byref_id_object_copy对自己内部的对象 Person *__strong weakPerson进行copy;根据前面的修饰符进行强引用还是弱引用,这里因为是strong,所以是强引用。同时注意一下,如果是mrc环境下,那么这个对person的引用就永远不会是强引用。

好下面看

typedef void (^Block)(void);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        
        Person *per = [[Person alloc] init];
        
        __block __weak Person *weakPerson = per;
        Block blok = ^{
            weakPerson.age = 20;
        };
        blok();
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

}
typedef void (*Block)(void);

struct __Block_byref_weakPerson_0 {
  void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__weak weakPerson;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_weakPerson_0 *weakPerson; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_weakPerson_0 *weakPerson = __cself->weakPerson; // bound by ref

            ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)(weakPerson->__forwarding->weakPerson), sel_registerName("setAge:"), 20);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        Person *per = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));

        __attribute__((__blocks__(byref))) __attribute__((objc_ownership(weak))) __Block_byref_weakPerson_0 weakPerson = {(void*)0,(__Block_byref_weakPerson_0 *)&weakPerson, 33554432, sizeof(__Block_byref_weakPerson_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, per};
        Block blok = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blok)->FuncPtr)((__block_impl *)blok);
    }
    return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));

}

跟上面不同的点就在于__Block_byref_weakPerson_0里面的 Person *__weak weakPerson;这里是weak修饰,同样形象化的以下图解释

证明是强指针:

证明有弱指针

在mrc环境下,证明copy也不会产生强指针。

arc下不论block里经过几层对person对象,都会有强弱指针。

block的内存管理,调用_Block_object_assign的时候传进去的值要么是3要么是8,但是__block修饰的 传进去的是__Block_byref_id_object_copy_131  这个 是131,这个管理的还是不同的。

总结:

 

5:循环引用

typedef void (^Block)(void);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        
        Person *per = [[Person alloc] init];
        per.age = 10;
        per.block = ^{
            
            NSLog(@"-----%d",per.age);
        };
    }
    NSLog(@"-----000000000");

    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 我们只看这一节

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__strong per;  
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

看到上面是__strong,也就是下面这个图

导致最后person对Person这个类的指针释放了,上面两个却一直相互引用,无法释放。

5.1:解决循环引用问题

arc:

__weak __unsafe_unretained解决  和__block

        Person *per = [[Person alloc] init];
        
        
        __weak typeof(per) weakPer = per;
        per.age = 10;
        per.block = ^{
            
            NSLog(@"-----%d",weakPer.age);
        };
Person *per = [[Person alloc] init];
        
        // __weak 不会产生强引用
        // __unsafe_unretained 不会产生强引用 不安全
        __unsafe_unretained typeof(per) weakPer = per;
        per.age = 10;
        per.block = ^{
            
            NSLog(@"-----%d",weakPer.age);
        };

weak会把指向的对象销毁时会自动置让指针为nil,所以weak是安全的。

__block

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        
        __block Person *per = [[Person alloc] init];
        
        // __weak 不会产生强引用
        // __unsafe_unretained 不会产生强引用 不安全
        per.age = 10;
        per.block = ^{
            
            NSLog(@"-----%d",per.age);
            
            per = nil;  // 这两个必须要写
        };
        per.block();   // 这两个必须要写 可能有内存泄漏,
    }
    NSLog(@"-----000000000");

    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

mrc环境下

__unsafe_unretained 和__block

mrc不支持__weak。

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        
        __unsafe_unretained Person *per = [[Person alloc] init];
        
        per.age = 10;
        per.block = [^{
            
            NSLog(@"-----%d",per.age);
            
        } copy];

        [per release];
    }
    NSLog(@"-----000000000");

    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-09-10 19:42:27.889 newxc[34540:11722615] person-dealloc----
2018-09-10 19:42:27.890 newxc[34540:11722615] -----000000000

__block在mrc下不会产生强引用对立面的对象。

__block Person *per = [[Person alloc] init];
        
        per.age = 10;
        per.block = [^{
            
            NSLog(@"-----%d",per.age);
            
        } copy];

        [per release];

大总结:面试

1:block的原理是怎样的,本质是什么?

封装了函数调用以及调用环境OC对象。

2:__block的作用?有什么使用的注意点?

封装成了一个对象,在mrc环境下,不会产生强引用对立面的对象

3:block的属性修饰词为什么是copy?使用block有哪些使用注意?

block没有copy,就不会在堆上,copy到堆上,就可以进行内存管理了,使用注意:循环引用

4:block在修改NSMUtablearray,需不需要添加__block?

不需要,

- (void)test
{
    __weak typeof(self) weakSelf = self;
    
    self.age = 10;
    self.block = ^{        
        
        NSLog(@"-----%d",weakSelf->_age);
        
    };

}

这个会报错,可能是会空值,不能产生强引用,可能会空值

所以就要用一个强指针,来骗编译器,这是编译器行为

- (void)test
{
    __weak typeof(self) weakSelf = self;
    
    self.age = 10;
    self.block = ^{
        
        __strong typeof(weakSelf) myself = weakSelf;
        
        
        NSLog(@"-----%d",myself->_age);
        
    };

}

另外 用这个强指针 可以确保不会保住这个对象。

猜你喜欢

转载自blog.csdn.net/qq_27909209/article/details/81514891