8.iOS开发之block

block简介

block(闭包)的本质是对象,是带有自动变量(局部变量)的匿名函数。

block与变量

值拷贝 会自动生成相应的属性来捕获外界变量,外部局部变量的变化不会影响它的的状态,block内部不能修改变量的值。

使用__block修饰符可以修改自动变量的值,是跟局部static变量一样指针传递

全局变量则是直接访问。

对于捕获ObjC对象,不同于基本类型,Block内部访问了对象类型的auto对象时,如果Block是在栈上,将不会对auto对象产生强引用当Block被拷贝到堆上,Block会调_Block_object_assign函数,根据auto对象的修饰符做出相应的操作;如果Block从堆上移除,会调用block内部的dispose函数,内部会调用_Block_object_dispose函数,这个函数会自动释放引用的auto对象。

解决block的循环引用

1.使用最简单的weak关键字修饰 

(会遇到在block中self还需要使用然而被释放)

可在block作用域内对weakSelf进行strong修饰的临时变量 weak-strong-dance强弱共舞

self.name = @"name";

    __weaktypeof(self) weakSelf =self;

    self.block= ^{

        __strongtypeof(weakSelf) strongSelf = weakSelf;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            NSLog(@"name:%@",strongSelf.name);

        });

    }

2.用__block修饰中间者变量使用,并在block中置为nil

 self.name = @"name";     __block ViewController *vc = self;     self.block = ^{         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{             NSLog(@"name:%@",vc.name);             vc = nil;         });     };

3.使用传参把self传入

self.name = @"name";     self.block = ^(ViewController *vc){         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{             NSLog(@"name:%@",vc.name);         });     };    

self.block(self);

Block与内存管理

根据Block在内存中的位置分为三种类型:

NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中。

NSStackBlock是位于栈区,超出变量作用域,栈上的Block以及  __block变量都被销毁。

NSMallocBlock是位于堆区,在变量作用域结束时不受影响。

Block的复制

在全局block调用copy什么也不做

在栈上调用copy那么复制到堆上

在堆上调用block 引用计数增加

Block从栈中复制到堆的几种情况

调用Block的copy实例方法时

Block作为函数返回值返回时

在带有usingBlock的Cocoa方法或者GCD的API中传递Block时候

将block赋给带有__strong修饰符的id类型或者Block类型时

Block的底层原理

研究工具:clang

block结构体成员:

isa,指向所属类的指针,也就是block的类型

Flags,标志变量,在实现block的内部操作时会用到

Reserved,保留变量

FuncPtr,block执行时调用的函数指针

包含了isa指针(包含isa指针的皆为对象),也就是说block也是一个对象(runtime里面,对象和类都是用结构体表示)。

你可能感兴趣的:(8.iOS开发之block)