iOS:block知识点小结

1、什么是block
block是将函数及其执行上下文封装起来的对象。
block调用实际就是函数调用。

2、变量截获
1、不截获全局变量和静态全局变量。
2、对于基础数据类型的局部变量,截获其值。
3、对于对象类型的局部变量,连同其所有修饰符一同截获。
4、以指针的形式截获局部静态变量。

cd 到文件所在目录后
执行 clang -rewrite-objc HFBlockObject.m,可查看编译后的c++代码,帮助理解。
若代码中使用了__weak,则执行
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 HFBlockObject.m

// 全局变量
int varIntOne = 100;
NSString *varStringOne = @"testOne";

// 全局静态变量
static int varStaticIntOne = 101;
static NSString *varStaticStringOne = @"testStaticOne";

@implementation HFBlockObject

- (void)testCaptureVariable {
    // 局部变量
    int tempA = 200;
    NSString *tempOne = @"tempOne";
    
    __weak NSString *tempWeakString = @"weakString";
    
    // 局部静态变量
    static int tempStaticA = 201;
    static NSString *tempStaticOne = @"tempStaticOne";
    
    void (^blockOne)(void) = ^ {
        
        NSLog(@"varIntOne = %d",varIntOne);
        NSLog(@"varStaticIntOne = %d",varStaticIntOne);
        NSLog(@"tempA = %d",tempA);
        NSLog(@"tempStaticA = %d",tempStaticA);
        
        NSLog(@"varStringOne = %@",varStringOne);
        NSLog(@"varStaticStringOne = %@",varStaticStringOne);
        NSLog(@"tempOne = %@",tempOne);
        NSLog(@"tempStaticOne = %@",tempStaticOne);
        
        NSLog(@"tempWeakString = %@",tempWeakString);

    };
    blockOne();
}

简化一下编译后的block是一个struct类型的对象

struct __HFBlockObject__testCaptureVariable_block_impl_0 {
  struct __block_impl impl;
  struct __HFBlockObject__testCaptureVariable_block_desc_0* Desc;
  int tempA;
  int *tempStaticA;
  NSString *__strong tempOne;
  NSString *__strong *tempStaticOne;
  NSString *__weak tempWeakString;
};
block 
基础成员:
结构体__block_impl 
结构体__HFBlockObject__testCaptureVariable_block_desc_0
其他成员:截获的变量

struct __block_impl {
  void *isa; // 指针
  int Flags;
  int Reserved;
  void *FuncPtr;// 指向block函数的指针,指向执行代码的位置
};

static struct __HFBlockObject__testCaptureVariable_block_desc_0 {
  size_t reserved;
  size_t Block_size; //block占的内存大小
} 

3、blcok的三种类型
全局block(GlobalBlock) - 存储在全局区
block内部使用的变量只能是局部或全局)静态变量和全局变量,不能是外部的局部变量或属性。
执行copy- - - >无效果 - 什么也没做

栈block(MallocBlock) - - 存储在栈区-
block内部使用了外部局部变量或属性,但没有将这个block赋值给强引用或用copy修饰的变量。
执行copy- - - >被拷贝到堆区

堆block(StackBlock) - -存储在在堆区 -
block内部使用了局部局部变量或属性,并将这个block赋值给强引用或用copy修饰的变量。
执行copy- - - >增加引用计数

如果一个对象的成员变量是block,若将栈上的block赋值给这个成员变量
若此成员变量没有使用copy关键字,当通过成员变量访问此block时,可能因为栈上内存被释放,导致访问异常,引起崩溃。使用copy关键字后,会复制到堆区,之后访问不会异常。
栈上block的销毁:变量作用域结束之后就被销毁
需要管理的是堆区block的内存。

4、__block
一般情况下,对被截获变量进行赋值(不等于使用)操作时需要加__block修饰符。
对变量进行赋值时
对于局部变量(基本数据类型/对象类型)修改时需要__block修饰符
对于静态全局变量/全部变量/静态局部变量修改时不需要__block修饰符

// 局部变量
    __block int tempA = 200; 
再次clang 后看源码
tempA 被__block修饰后变为结构体对象,temp变成这个新对象的成员变量。

struct __Block_byref_tempA_0 {
  void *__isa; // isa指针,表示这是一个对象
__Block_byref_tempA_0 *__forwarding;//指向自身的指针
 int __flags;
 int __size;
 int tempA; //变量值 
};

__block 修饰后局部变量可以修改的原因是,从原来的传递数值,变成了传递指针,因此可以在block内部修改(对比两个包含方法入口的截图中tempA就可发现)。

__forwarding指针
栈上block的__forwarding指针指向的是它copy出的堆上的__block变量,
若栈上的block无copy,它的__forwarding指针指向的还是这个栈上的__block变量。
__forwarding存在的意义:无论做任何内存位置,都可以顺利访问到同一个__block变量

__block MRC/ARC下的区别


MRC下不会产生循环引用,ARC下会产生循环引用产生内存泄漏。
ARC下的循环引用:

上面的解决方案是在return之前设置blockSelf = nil,就断开了__block变量和对象之前的持有连结。
但是这样解决弊端是:如果这个block不被调用,这个循环就会一直存在。
5、循环引用
产生原因:
block是vc的属性,那么vc就强持有block。若block内使用vc其他属性,就强持有vc,这样就产生了循环引用。
解决方法:
1、weak - strong

2、self当作参数传递给block
这样self就相当于局部变量,block执行结束后会自动销毁。这种操作不涉及到捕获变量。

6、下面代码会有什么问题

void(^__weak weakBlock)(void) = nil;

- (void)viewDidLoad {
    [super viewDidLoad];

    [self blockStackA];
//执行weakBlock的时候崩溃
    weakBlock();
}

-(void)blockStackA {
    int a = 0;
    int b = 2;
    // weakBlock1存在栈区
    void(^__weak weakBlock1)(void) = ^ {
        NSLog(@"b - - - = %d",b);
    };
    a = b;
// block是引用类型,赋值是多个指针指向栈区block
    weakBlock = weakBlock1;
    NSLog(@"weakBlock = %@, weakBlock1 = %@",weakBlock,weakBlock1);
    NSLog(@"weakBlock = %p, weakBlock1 = %p",&weakBlock,&weakBlock1);
//栈区的block在函数作用域内可正常执行,出了函数作用域,就被释放。
//可正常执行
    weakBlock();
}

7、block 访问宏会循环引用吗?
不会。
宏是预编译的时候被替换,不是全局静态变量。

8、常见的第三方库:Masonry、MJRefresh等,使用self,会不会产生强引用?
Masonry不会,MJRefresh调用内部方法不会。内部使用了弱引用(weak)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352