说明:阅读本文章,请参考之前的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. 根据自定义区域裁剪ArcGIS切片地图服务

    切片地图服务是访问地图最快捷的服务方式.假如要根据地理区域对切图进行访问控制,往往只能针对不同地理区域制作相应的地图,并发布为切片地图服务.而一般在切图的时候又是按全区域实施的,所以给切片管理者造成不 ...

  2. screen 状态为Attached 连不上

    用 screen -ls, 显式当前状态为Attached, 但当前没有用户登陆些会话.screen此时正常状态应该为(Detached)  此时用screen -r ,怎么也登不上. 最后找到解决方 ...

  3. oracle 用户创建、修改、删除

    创建用户: create user test identified by test; 修改密码: 1.alter user test identified by mima; 2.passw[ord]  ...

  4. 微信小程序接入腾讯云IM即时通讯(会话列表)

    会话列表功能概述: 登录 :先用自己的账号登录腾讯云: 获取会话列表 :登录之后再获取会话列表: 更新未读消息数量 :获取会话列表之后更新未读消息数量 WXML代码(自己写的将就看一下) <vi ...

  5. 《Think in JAVA》之每日一读(initianlize)——2013/11/12、13

    了解包括继承在内的初始化全过程,以对所发生的的一切有一个全局的把握,是很有益的. 请看下例: package initialize; class Insect { private int i = 9; ...

  6. 优化 ExpressRoute 路由

    当你有多个 ExpressRoute 线路时,可以通过多个路径连接到 Azure.结果就是,你所采用的路由可能不是最理想的 - 也就是说,你的流量可能会经历较长的路径才能到达 Azure,而 Azur ...

  7. HTML--<frameset>標簽

    <html><frameset rows="20,80"> <frame src="/example/html/frame_a.html&q ...

  8. 数组(list)分组、分段

    对一个list进行分组,要求控制每组中的元素个数: 1.使用切片分组: lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1] #lst可为 ...

  9. 微信自定义菜单的emoji图标

    微信公众号自定义菜单添加emoji表情图标 第一步:打开微信公众平台接口调试工具,点击前往接口调试工具: 第二步:把这段代码  {"button":[{"sub_butt ...

  10. Linux 系统必须掌握的文件_【all】

    0.Linux 系统文件的详解 1.Linux 系统的网络配置文件 2.Linux 系统的DNS配置文件 3.Linux 系统的IP与域名解析文件[局域网的DNS] 4.Linux 系统的主机别名文件 ...