Block专辑:

Block讲解一

MRC-block与ARC-block

本篇博客不再讲述Block的基本定义使用,最近而是看了很多的block博客讲述的太乱太杂,所以抽出时间整理下block的相关底层知识,在讲述之前,提出几个问题,如果都可以回答出来以及知道原理,大神绕过,反之,希望本篇博客对大家面试或者block不熟悉者有所帮助,以后会不断更新博客,欢迎关注和指正!!!

  1. blcok的原理是怎样的?本质又是什么?
  2. __block的作用是什么?有什么使用注意点?
  3. block的属性修饰词为什么是copy修饰?使用block有哪些使用注意事项?
  4. block在修改NSMutableArray,需要不需要添加__block?

一、 block本质

  • blcok本质是OC对象,它内部也有个isa指针,在OC中有isa指针的对象,可以认定为OC对象
  • block对象是封装了函数调用以及函数调用环境的OC对象

通过下面例子看下block结构

文件结构

int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
void (^block)(int, int) = ^(int a, int b){
NSLog(@"This is a block --%d",age);
NSLog(@"This is a block");
NSLog(@"This is a block");
};
block(10, 20);
}
return 0;
}

通过clang编译器将OC代码编译成C语言代码,并生成了在后缀名为.cpp的C++文件中,clang命令为

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

然后查看编译出来的c++ main.cpp文件,和main.m同一个地方,将它移入到项目中,并在Build Phases->Compile Sources中删除main.cpp,然后就可以编译成功

这次开始查看main.cpp的,对比两个文件,找出main.cpp的对应的main函数

看到上面main.cpp左边的调用block,10,20前面有很多的强制类型转换,最后可以是funcPtr (block,10,20)在.cpp中海油一个__main_block_imp_0地址,查看其地址内容

二、block变量捕获机制

举例1: block变量捕获-auto变量

经常书写int age = 10,前面都是有默认关键字auto的(也可以不书写,经常这样的),下面的结果是什么?

int age = 10 等价于 auto int age = 10 (auto自动变量,离开作用域就会销毁)

int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = ;
void (^block)(void) = ^{
NSLog(@"age is %d", age);
};
age = ;
block();
}
return ;
}

猜一下运行结果

再次运行查看编译出来的main.cpp,重复上面的步骤,对比main.cpp

查看__main_block_impl_0的结构,传入的参数age = 10,block内部新增了一个变量用于存储age

block的内部的age = 10 ,并不会改变,所以打印结果为10(当创建block的内容,age = 10已经存在了block中,并不会随外部改变而改变)

举例2:block变量捕获-static变量

int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int age = ;
static int height = ;
void (^block)(void) = ^{
NSLog(@"age is %d, height is %d", age, height);
};
age = ;
height = ;
block();
}
return ;
}

结果为age = 10,height = 20

将其编译为cpp,对比下

查看__main_block_impl_0的代码结构

发现block捕获到了age和height,所以block会捕获到局部变量,而静态局部变量block存放的是地址,所以未来修改height的值时,取出的是所指向的最新的height值

举例3:block变量捕获-全局变量

int age = ;
static int height = ;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"age is %d, height is %d", age, height);
};
age = ;
height = ;
block();
}
return ;
}

查看.cpp文件有没有捕获到全局变量?--直接访问

 对于上面的block变量捕捉机制,总结如下:

三、block的类型

block的底层结构如下

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

到底什么类型的block属于__NSGlobalBlock__,__NSStackBlock__, __NSMallocBlock__?

先不铺垫那么多,直接给出结论:

下面来一一验证结论

就是像上面提问那样,按总结的访问了auto变量应该是NSStackBlock,怎么成为了NSMallocBlock了呢?

这是因为编译器默认在ARC环境下,应该切换到MRC环境下,看一下真正的block类型,至于ARC的,下篇讲述!!!

将编译器改为去除ARC,在Build Settings -> automatic Reference Counting 中将ARC改为MRC

改成MRC后再次允许查看结果

那什么时候是NSMallocBlock呢?

NSStackBlock调用copy变为NSMallocBlock,但是NSGlobalBlock调用了copy依然是NSGlobalBlock,NSMallocBlock调用了copy方法引用计数会+1

那么在ARC环境下什么时候block会调用copy呢?(从栈空间->堆空间)

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如有以下情况:

  • block作为函数返回值时
  • block赋值给__strong指针时
  • block作为Cocoa API中方法名有usingBlock的方法参数时 如:[array enumeratorObjectsUsingBlock]
  • block作为GCD API的方法参数时

四、对象类型的auto变量

上面讲述auto变量是Int等基本类型,现在改成对象类型,如Person类对象[ARC环境下面]

 typedef void(^ZXYBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZXYBlock block ;
{
Person *person = [[Person alloc]init];
person.age = ;
block = ^{
NSLog(@"---------%d", person.age);
};
}
NSLog(@"block执行完");
}
return ;
} @interface Person : NSObject
@property (nonatomic, assign) int age;
@end @implementation Person
-(void)dealloc {
NSLog(@"person对象已释放");
}
@end

将上面的代码打breakPoint断点在12行处,查看Person对象是否在打括号{}内释放

发现在打印之前并没有释放person对象,猜想block引用了person,导致block执行完之后才被释放(block当autoReleasePool执行完之后才会被释放) 查看c++代码

查看main函数调用

通过上面查看结构体struct __main_block_desc 里面多了两个copy和dispose(相当于retain)对person进行捕捉到age变量,当block不被释放,person对象也不会被释放

当断点改到14行,执行完block时,查看结果

block被释放,完成打印释放

 总结:对象访问的auto变量

当block内部访问了对象类型的auto变量时

  • 如果block在栈上,将不会对auto变量产生强引用
  • 如果block被拷贝到堆上
  1. 会调用block内部的copy函数
  2. copy函数内部会调用Block_object_assign 函数
  3. Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

  • 如果block从堆上移除
  1. 会调用block内部的dispose函数
  2. dispose函数内部会调用_Block_object_dispose函数
  3. _Block_object_dispose函数会自动释放引用的auto变量

以上就是block详解一的内容,下一篇讲述block剩下的知识点,欢迎关注!!!

Block详解一(底层分析)的更多相关文章

  1. Block详解二(底层分析)

    Block专辑: Block讲解一 MRC-block与ARC-block Block详解一(底层分析) 今天讲述Block的最后一篇,后两篇仅仅是加深1,2篇的理解,废话少说,开始讲解! __blo ...

  2. LinkedList详解-源码分析

    LinkedList详解-源码分析 LinkedList是List接口的第二个具体的实现类,第一个是ArrayList,前面一篇文章已经总结过了,下面我们来结合源码,学习LinkedList. 基于双 ...

  3. iOS开发——Block详解

    iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...

  4. 关于syslog日志功能详解 事件日志分析、EventLog Analyzer

    关于syslog日志功能详解 事件日志分析.EventLog Analyzer 一.日志管理 保障网络安全 Windows系统日志分析 Syslog日志分析 应用程序日志分析 Windows终端服务器 ...

  5. Java性能分析之线程栈详解与性能分析

    Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...

  6. ArrayList详解-源码分析

    ArrayList详解-源码分析 1. 概述 在平时的开发中,用到最多的集合应该就是ArrayList了,本篇文章将结合源代码来学习ArrayList. ArrayList是基于数组实现的集合列表 支 ...

  7. UEFI启动视频详解:启动分析+N项操作实例

    ============================================================= ※※※※最给力的视频解说※※※※ 2011hiboy全部共享资料:立刻去   ...

  8. Shiro的Filter机制详解---源码分析

    Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...

  9. Shiro的Filter机制详解---源码分析(转)

    Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...

随机推荐

  1. 制作MACOSX 10.9Mavericks安装启动U盘教程

    苹果OS X Mavericks发布了,不少朋友又开始制作系统安装U盘了.其实下面介绍的方法与之前介绍的方法可能有些不同,不过原理还是一样的.下面看看具体的操作步骤吧! 1. 首先你需要开启显示隐藏文 ...

  2. PyTorch模型加载与保存的最佳实践

    一般来说PyTorch有两种保存和读取模型参数的方法.但这篇文章我记录了一种最佳实践,可以在加载模型时避免掉一些问题. 第一种方案是保存整个模型: 1 torch.save(model_object, ...

  3. VRRP笔记三:配置keepalived为实现haproxy高可用的双主模型配置文件示例:

    说明:其基本实现思想为创建两个虚拟路由器,并以两个节点互为主从. ! Configuration File for keepalived global_defs { notification_emai ...

  4. Web of science|SCIE|影响因子|SSCI|高被引论文|领域中热点论文|

    信息检索: 数据库 Web of science 影响因子只是针对期刊打分,并不是对文章打分.所以对文章评价可以看引用次数. SCIE (Science Citation Index Expanded ...

  5. cesium入门示例-3dTiles加载

    数据转换工具采用cesiumlab1.5.17版本,转换后的3dTiles加载显示比较简单,通过Cesium.Cesium3DTileset接口指定url即可,3dTiles文件可与js前端代码放置一 ...

  6. SpringBoot + JPA + mariadb

    SpringBoot + JPA + MariaDB 源码:https://github.com/ldl326308/LiveGreen-SpringBoot.git JPA持久层方法名命名规则:

  7. Luogu_2878_[USACO07JAN]保护花朵Protecting the Flowers

    题目描述 Farmer John went to cut some wood and left N (2 ≤ N ≤ 100,000) cows eating the grass, as usual. ...

  8. Dubbo之心跳机制 · 房东的小黑

    在网络传输中,怎么确保通道连接的可用性是一个很重要的问题,简单的说,在网络通信中有客户端和服务端,一个负责发送请求,一个负责接收请求,在保证连接有效性的背景下,这两个物体扮演了什么角色,心跳机制能有效 ...

  9. BeWhatever

    Hadoop Distributed File System:分布式文件系统. HDFS基于流数据模式访问和处理超大文件需求开发,具有高容错性,高可靠性,高可扩展性,多部署在低成本的硬件上.HDFS提 ...

  10. C++扬帆远航——19(斐波那契数列第20项)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:fib.cpp * 作者:常轩 * 微信公众号:Worldhel ...