说明:阅读本文章,请参考之前的block文章加以理解;

一、栈区block分析

//代码

//ARC
void test1()
{
{
Person *per = [[Person alloc] init];
per.age = ;
^{
NSLog(@"age:%d", per.age);
};
} NSLog(@"-------1");
}

//打印

-- ::12.118653+ MJ_TEST[:] Person dealloc
-- ::12.118934+ MJ_TEST[:] -------
Program ended with exit code:  

分析:

<1>block代码内部引用的Person实例对象先于输出语句销毁,因为per仅限于大括号内,但此时block销毁了没有?往下看;

<2>上述block代码块并没有被指针持有,接下来看看指针持有的情况;

//代码

typedef void(^MyBlock)(void);

//ARC
void test2()
{
MyBlock block; {
Person *per = [[Person alloc] init];
per.age = ;
block = ^{
NSLog(@"age:%d", per.age);
};
} NSLog(@"-------1");
}

//打印

-- ::58.473267+ MJ_TEST[:] -------
-- ::58.473705+ MJ_TEST[:] Person dealloc
Program ended with exit code:

分析:Person实例对象后于输出语句销毁,为什么有指针持有,顺序就变了?

<1>等号左边:是一个auto类型的局部的block指针变量,存放在栈区;等号右边:是一个block代码块(对象),也是一个局部对象,存放在栈区;

<2>在ARC模式下,如果有指针持有(默认是强指针,修饰符为__strong)一个局部的block对象,系统会自动copy该block对象从栈区到堆区;

补充:其他三种情况——block作为函数返回值、含usingBlock方法(如数据的枚举方法)、GCD的应用(自己可以验证,此处不再赘述);

那么,我们再看看MRC的情况

//打印————test1()和test2()

-- ::46.641171+ MJ_TEST[:] -------
Program ended with exit code:

分析:为什么per对象没有销毁?——因为需要手动释放;

//代码

[per release];

//打印————test1()和test2()

-- ::39.091313+ MJ_TEST[:] Person dealloc
-- ::39.091974+ MJ_TEST[:] -------
-- ::39.092013+ MJ_TEST[:] Person dealloc
-- ::39.092086+ MJ_TEST[:] -------
Program ended with exit code:

分析:

<1>此时的block对象的作用域在第一个大括号范围内,超出则被释放;

<2>Person实例对象被捕获到block对象结构体体中,同时其作用域也仅限于第一个大括号内,因此超出同样被释放;

二、堆区block分析

1)类型分析——ARC

//strong类型

执行上述test2()方法,我们知道系统会自动将block对象从栈区copy到堆区;同时,Person实例对象会被捕捉到block对象的结构体中,如下

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

分析:可以看到per是一个指针变量,而该指针变量默认修饰符为__strong;修改代码

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

clang命令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-9.0.0 main.m

说明:

<1>该命令行,只针对ARC模式下,MRC模式下,如果有release语句会报错;

<2>该命令行,是解决ARC模式下,实例对象为__weak类型,转成C++代码;

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

分析:因此,一般的指针变量,默认修饰符为__strong;

//weak类型

//代码

//ARC
void test2()
{
MyBlock block; {
Person *per = [[Person alloc] init];
per.age = ;
__weak Person *weakPer = per;
block = ^{
NSLog(@"age:%d", weakPer.age);
};
// [per release];
} NSLog(@"-------1");
}

//C++代码

struct __test2_block_impl_0 {
struct __block_impl impl;
struct __test2_block_desc_0* Desc;
Person *__weak weakPer;
__test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, Person *__weak _weakPer, int flags=) : weakPer(_weakPer) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

//打印

-- ::36.280746+ MJ_TEST[:] Person dealloc
-- ::36.281063+ MJ_TEST[:] -------
Program ended with exit code:

分析:

<1>此时的per由weakPer弱指针指向,并被捕捉到block对象结构体中;

<2>结合上述强指针引用Person实例对象的打印结果,weak修饰的指针变量先于输出语句销毁,可以肯定系统是没有对block对象copy到堆区的;

那针对不同类型指针的引用,系统是如果判断操作的呢?往下看

2)调用原理

//C++代码

typedef void(*MyBlock)(void);

struct __test2_block_impl_0 {
struct __block_impl impl;
struct __test2_block_desc_0* Desc;
Person *__weak weakPer;
__test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, Person *__weak _weakPer, int flags=) : weakPer(_weakPer) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
}; static void __test2_block_func_0(struct __test2_block_impl_0 *__cself) {
Person *__weak weakPer = __cself->weakPer; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_tb_zgsq5gq15rd3zvbdmw1c09y80000gn_T_main_3e374f_mi_2, ((int (*)(id, SEL))(void *)objc_msgSend)((id)weakPer, sel_registerName("age")));
} static void __test2_block_copy_0(struct __test2_block_impl_0*dst, struct __test2_block_impl_0*src) {_Block_object_assign((void*)&dst->weakPer, (void*)src->weakPer, /*BLOCK_FIELD_IS_OBJECT*/);} static void __test2_block_dispose_0(struct __test2_block_impl_0*src) {_Block_object_dispose((void*)src->weakPer, /*BLOCK_FIELD_IS_OBJECT*/);} static struct __test2_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __test2_block_impl_0*, struct __test2_block_impl_0*);
void (*dispose)(struct __test2_block_impl_0*);
} __test2_block_desc_0_DATA = { , sizeof(struct __test2_block_impl_0), __test2_block_copy_0, __test2_block_dispose_0}; void test2()
{
MyBlock block; {
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"));
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)per, sel_registerName("setAge:"), );
__attribute__((objc_ownership(weak))) Person *weakPer = per;
block = ((void (*)())&__test2_block_impl_0((void *)__test2_block_func_0, &__test2_block_desc_0_DATA, weakPer, )); } NSLog((NSString *)&__NSConstantStringImpl__var_folders_tb_zgsq5gq15rd3zvbdmw1c09y80000gn_T_main_3e374f_mi_3);
} int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; test2();
}
return ;
}

分析:

<1>根据前面的文章分析,我们发现block描述结构体(__test2_block_desc_0)中多了两个函数指针的成员变量

void (*copy)(struct __test2_block_impl_0*, struct __test2_block_impl_0*);
void (*dispose)(struct __test2_block_impl_0*);

其中,copy函数指针指向__test2_block_copy_0函数,dispose指向__test2_block_dispose_0;

<2>__test2_block_copy_0函数主要是通过_Block_object_assign函数来确定对per对象是否强引用,其根据就是per的引用类型——如果是__strong类型,则block对象对per对对象进行强引用(per的生命周期可控);如果是__weak类型,则进行弱引用(per的生命周期不可控);

<3>当block对像从堆区销毁时,会调用__test2_block_dispose_0函数会自动释放引用的per对象(相当于release)——注:严格意义上,此处的释放指的断开是block对象对per的引用即retainCount减1,至于per对象所占的内存是否被释放(回收)则在所不问(也许还有其他的指针变量引用),只有retainCount变为0零,其内存才会被回收;

补充:当block访问的外部的auto类型的局部数据为对象时,则会产生上述两个函数指针;如果是非实例对象(如基础数据类型),则不会有上述两个函数指针——原因:实例对象一般是在堆区开辟的内存,需要对其进行内存管理————注:如果是__block修饰前述变量(包括实例对象),也会产生上述两个指针函数,具体后面文章会写到!

//代码

void test3()
{
int age = ;
^{
NSLog(@"%d", age);
};
}

//clang

static struct __test3_block_desc_0 {
size_t reserved;
size_t Block_size;
} __test3_block_desc_0_DATA = { , sizeof(struct __test3_block_impl_0)};

三、结论

【1】栈区block:不论是ARC还是MRC模式,指向该block对象的指针变量,不会对引用的auto类型的局部的实例对象进行强引用;

【2】堆区block:不论是ARC还是MRC模式,指向该block对象的指针变量,根据引用的auto类型的局部的实例对象的引用类型,通过调用block结构体中的copy函数指针来调用_Block_object_assign函数,来决定对实例对象是否进行强引用——__strong类型强引用,__weak类型弱引用;

【3】堆区block释放:系统会通过调用block结构体中的dispose函数指针来调用__test2_block_dispose_0函数,自动释放引用的外部auto类型的局部实例对象;

说明:

<1>block对象本身,即代码块(位于等号右边),非block指针变量(位于等号左边);

<2>所谓的强引用,类似于retain操作即保留实例对象(所占内存不随作用域限制而被自动回收),保证手动管理内存释放,达到可控的目的;

四、拓展——GCD引用分析

1)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", per);
}); NSLog(@"touchesBegan");
}

//打印

-- ::19.867668+ GCD_Refrence[:] touchesBegan
-- ::22.867823+ GCD_Refrence[:] <Person: 0x600003bb8cf0>
-- ::22.867996+ GCD_Refrence[:] Person dealloc

2)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakPer);
}); NSLog(@"touchesBegan");
}

//打印

-- ::58.381396+ GCD_Refrence[:] touchesBegan
-- ::58.381583+ GCD_Refrence[:] Person dealloc
-- ::01.381697+ GCD_Refrence[:] (null)

3)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", per);
});
}); NSLog(@"touchesBegan");
}

//打印

-- ::44.996108+ GCD_Refrence[:] touchesBegan
-- ::48.088426+ GCD_Refrence[:] <Person: 0x6000010f4a20>
-- ::48.088664+ GCD_Refrence[:] Person dealloc

4)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakPer);
});
}); NSLog(@"touchesBegan");
}

//打印

-- ::42.122836+ GCD_Refrence[:] touchesBegan
-- ::42.123038+ GCD_Refrence[:] Person dealloc
-- ::45.123256+ GCD_Refrence[:] (null)

5)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakPer); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", per);
});
}); NSLog(@"touchesBegan");
}

//打印

-- ::50.591355+ GCD_Refrence[:] touchesBegan
-- ::51.685830+ GCD_Refrence[:] <Person: 0x6000033a4470>
-- ::53.686541+ GCD_Refrence[:] <Person: 0x6000033a4470>
-- ::53.686810+ GCD_Refrence[:] Person dealloc

6)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", per); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakPer);
});
}); NSLog(@"touchesBegan");
}

//打印

-- ::47.349637+ GCD_Refrence[:] touchesBegan
-- ::48.447971+ GCD_Refrence[:] <Person: 0x600000163900>
-- ::48.448271+ GCD_Refrence[:] Person dealloc
-- ::50.448553+ GCD_Refrence[:] (null)

7)

//代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", per); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*), dispatch_get_main_queue(), ^{
NSLog(@"%@", per);
});
}); NSLog(@"touchesBegan");
}

//打印

-- ::37.584067+ GCD_Refrence[:] touchesBegan
-- ::38.679922+ GCD_Refrence[:] <Person: 0x600003bd7570>
-- ::40.876560+ GCD_Refrence[:] <Person: 0x600003bd7570>
-- ::40.876803+ GCD_Refrence[:] Person dealloc

8)

//代码

- (void)test8
{
Person *per = [[Person alloc] init];
__weak Person *weakPer = per;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*3), dispatch_get_main_queue(), ^{
NSLog(@"1---weakPer--%@", weakPer); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*2), dispatch_get_main_queue(), ^{
NSLog(@"2---per--%@", per); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*2), dispatch_get_main_queue(), ^{
NSLog(@"3---weakPer--%@", weakPer); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC*2), dispatch_get_main_queue(), ^{
NSLog(@"4---per--%@", per); });
});
});
}); NSLog(@"touchesBegan");
}

//打印

2019-05-27 10:27:15.314844+0800 2-2 auto类型局部实例对象GCD[971:43547] touchesBegan
2019-05-27 10:27:18.315007+0800 2-2 auto类型局部实例对象GCD[971:43547] 1---weakPer--<Person: 0x60000392ab30>
2019-05-27 10:27:20.315417+0800 2-2 auto类型局部实例对象GCD[971:43547] 2---per--<Person: 0x60000392ab30>
2019-05-27 10:27:22.315839+0800 2-2 auto类型局部实例对象GCD[971:43547] 3---weakPer--<Person: 0x60000392ab30>
2019-05-27 10:27:24.316280+0800 2-2 auto类型局部实例对象GCD[971:43547] 4---per--<Person: 0x60000392ab30>
2019-05-27 10:27:24.316524+0800 2-2 auto类型局部实例对象GCD[971:43547] Person dealloc

分析:

<1>ARC环境下,使用GCD时,系统自动将block对象从栈区copy到堆区;

<2>根据以上打印结果,发现如果是单纯的对per进行强引用,则延时3秒后per对象才销毁;如果是弱引用,则立即销毁,再次使用时为空(此时已经被释放);

<3>如果既对per强引用又有弱引用,在嵌套的GCD使用中,以最后一个强引用为准——即per对象在最后一个强引用执行完后就会释放(之后的弱引用则输出为空);

结论:同一个auto类型的局部的实例对象,既有强引用,也有弱引用,以强引用为准;

GitHub

block本质探寻五之atuto类型局部实例对象的更多相关文章

  1. block本质探寻二之变量捕获

    一.代码 说明:本文章须结合文章<block本质探寻一之内存结构>和<class和object_getClass方法区别>加以理解: //main.m #import < ...

  2. block本质探寻六之修改变量

    说明: <1>阅读本文章,请参照前面的block文章加以理解: <2>本文的变量指的是auto类型的局部变量(包括实例对象): <3>ARC和MRC两种模式均适用: ...

  3. block本质探寻七之内存管理

    说明: <1>阅读本问,请参照block前述文章加以理解: <2>环境:ARC: <3>变量类型:基本数据类型或者对象类型的auto局部变量: 一.三种情形 //代 ...

  4. block本质探寻一之内存结构

    一.代码——命令行模式 //main.m #import <Foundation/Foundation.h> struct __block_impl { void *isa; int Fl ...

  5. block本质探寻八之循环引用

    说明:阅读本文,请参照之前的block文章加以理解: 一.循环引用的本质 //代码——ARC环境 void test1() { Person *per = [[Person alloc] init]; ...

  6. block本质探寻四之copy

    说明: <1>阅读本文,最好阅读之前的block文章加以理解: <2>本文内容:三种block类型的copy情况(MRC).是否深拷贝.错误copy: 一.MRC模式下,三种b ...

  7. block本质探寻三之block类型

    一.oc代码 提示:看本文章之前,最好按顺序来看: //代码 void test1() { ; void(^block1)(void) = ^{ NSLog(@"block1----&quo ...

  8. Java基础 -- 深入理解Java类型信息(Class对象)与反射机制

    一 RTTI概念 认识Claa对象之前,先来了解一个概念,RTTI(Run-Time Type Identification)运行时类型识别,对于这个词一直是 C++ 中的概念,至于Java中出现RT ...

  9. 五.OC基础--1.多态,2.类对象,3.点语法,4.@property&@synthesize,5.动态类型,内省(判断对象是否遵循特定的协议,以及是否可以响应特定的消息)

    五.OC基础--1.多态, 1. 多态概念,定义:多态就是某一类事物的多种形态: 表现形式: Animal *ani = [Dog new]; 多态条件:1.有继承关系 2.有方法的重写 2.多态代码 ...

随机推荐

  1. 基于OpenMP的C++并行编程简单示例

    示例要求:在整数A和B之间找到符合条件的值X,使f(X)=C. 示例代码(需要在VS中开启OpenMP支持): #include<iostream> #include<time.h& ...

  2. CVE-2017-17215 - 华为HG532命令注入漏洞分析

    前言 前面几天国外有个公司发布了该漏洞的详情.入手的二手 hg532 到货了,分析测试一下. 固件地址:https://ia601506.us.archive.org/22/items/RouterH ...

  3. Elasticsearch入坑指南之RESTful API

    Elasticsearch入坑指南之RESTful API Tags:Elasticsearch ES为开发者提供了非常丰富的基于Http协议的Rest API,通过简单的Rest请求,就可以实现非常 ...

  4. WinPE ISO制作

    1.安装ADK,然后导出winPE镜像文件和启动文件: 打开 部署和映像工具环境,cd "Windows Preinstallation Environment",运行 copyp ...

  5. Allure 安装及使用

    linux下安装方法 Allure requires Java 8 or higher    npm install -g allure-commandline --save-dev (如果npm不能 ...

  6. CSS学习摘要-定位实例

    CSS学习摘要-定位实例 注:全文摘自MDN-CSS定位实例 列表消息盒子 我们研究的第一个例子是一个经典的选项卡消息框,你想用一块小区域包括大量信息时,一个非常常用的特征.这包括含有大信息量的应用, ...

  7. C++:sprintf()的用法(转)

    转:http://blog.csdn.net/masikkk/article/details/5634886 更多:http://blog.csdn.net/zjuwispersure/article ...

  8. 事后诸葛亮之Alpha十天冲刺之失败总结

    参考自构建之法p341页的模板 首先自己预计了一下,项目gg的可能有百分之50这里面有百分之80是我的责任.冲刺失败我承担主要责任. 1.设想和目标: 1.计划实现类似华为云的小功能之团队合作开发功能 ...

  9. 【Alpha】Daily Scrum Meeting——blog1

    团队成员 吴吉键 201421122007(组长) 魏修祺 201421122008 孙劲林 201421122022 1. 会议当天照片 忘记拍了,能补上不? 2. 每个人的工作 3. 燃尽图

  10. adb shell 运行时报错"adb server version (26) doesn't match this client (39); killing..."的解决方案

    adb即 Android Debug Bridge 是一个通用的命令行工具,可用于通过PC端对连接的Android模拟器设备或连接至电脑的真实物理设备进行命令行操作.目前,许多软件均会借助 adb 工 ...