欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ios Block

程序员文章站 2024-02-29 22:30:46
...

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]));
    }
}

ios Block

ios Block

所以上面的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

源码

ios Block

 

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

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

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

总结如下。

ios Block

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

#import "Person.h"

@implementation Person

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

答案会捕获。

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

ios Block

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

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进行捕获。看源码

ios Block

问题:如果是[self name];

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

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

ios Block

ios Block

 

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编译器里面的一部分)从上到下是高地址到低地址。

ios Block

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

先看一下block的类型

 ios 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:小总结

ios Block

在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,就会释放引用的那些函数

看总结图

ios Block

看题

ios Block

ios Block

ios Block

ios Block

ios Block

苹果会认为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;
}

 

ios Block

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


        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赋值,是修改成功的)。

ios Block

ios Block


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

总结

ios Block

那如果再加上一个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)

ios Block

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

ios Block

(这个内存管理对没有修饰的对象类型的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到堆上了,地址肯定就发生变化了。)

ios Block

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来修饰的,再对它产生强或者弱引用。

那相同点是什么呢?

ios Block

 

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操作。

ios Block

所以 过程是:

当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的引用就永远不会是强引用。

ios Block

ios Block

好下面看

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修饰,同样形象化的以下图解释

ios Block

证明是强指针:

ios Block

证明有弱指针

ios Block

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

ios Block

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

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

总结:

ios Block

 

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,也就是下面这个图

ios Block

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

ios Block

 

5.1:解决循环引用问题

arc:

__weak __unsafe_unretained解决  和__block

ios 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]));
}

ios Block

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];

ios Block

 

大总结:面试

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);
        
    };

}

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

ios Block

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

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

}

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

相关标签: block