block理解__weak __strong 捕获变量的瞬时值

2019-12-09  本文已影响0人  惊蛰_e3ce
疑问:block本身的内存管理和引用计数管理 

static变量会被多加一个* 获取根内存地址
NSMallocBlock只需要对NSStackBlock进行copy操作就可以获取,但是retain操作就不行,会在下面说明
Block的copy、retain、release操作 (还是copy一段)
不同于NSObjec的copy、retain、release操作:
Block_copy与copy等效,Block_release与release等效;
对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
NSGlobalBlock:retain、copy、release操作都无效;
NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;

stackBlock不能直接放入array 出栈就释放。

Block对外部变量的存取管理

arc strong retain 都ok
//
// The -fobjc-arc flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
//
id objc_retainBlock(id x) {
return (id)_Block_copy(x);
}

block从栈复制到堆会对引用的对象发送copy消息
__main_block_impl_0

__main_block_desc_0* Desc

void (copy)(struct __main_block_impl_0, struct __main_block_impl_0*);

void (dispose)(struct __main_block_impl_0);

static void __main_block_copy_0(struct __main_block_impl_0dst, struct __main_block_impl_0src) {_Block_object_assign((void)&dst->tempLabel, (void)src->tempLabel, 3/BLOCK_FIELD_IS_OBJECT/);}

static void __main_block_dispose_0(struct __main_block_impl_0src) {_Block_object_dispose((void)src->tempLabel, 3/BLOCK_FIELD_IS_OBJECT/);}

__block
arc下回对变量转成结构体对象外还会加上对应的strong weak 所以无法解决循环引用 (block调用里面置位nil 比较low)
mrc下是赋值?不会强引用 所以可解决循环引用


* 引用局部(非静态)变量的block是NSStackBlock  
 * 没引用的是NSGlobalBlock  
* arc下系统会自动把NSStackBlock复制到堆上成NSMallocBlock 
* arc下用weak修饰的block是NSStackBlock 匿名没被接收;
* 如果block中没有引用外部变量,那么就会直接是个NSConcreteGlobalBlock 而不论外部引用的指针是不是强引用
* 正常情况copy操作不管MRC或者ARC都在堆区,只是在MRC下进行copy会改变地址从栈区复制到堆区  arc下新指针而已(weak修饰copy也会新地址)
* strong修饰ARC和MRC都并没有问题, 但是assign和retain在MRC环境下是还是在栈区的,会有问题(过作用域释放了 在调用会报异常)
* MRC下 NSStackBlock __block修饰的变量,并不改变引用计数(栈block 是赋值),同时block内部并不对引入的外部对象,更改引用计数。
* 在arc中, __block修饰的变量会被引用到,并且计数+1(复杂的结构体强指针)
* ARC下block会被修改为__NSMallocBlock__ ,同时引用计数增加了(栈复制到堆)。
* MRC下NSMallocBlock  __block修饰的变量,并不改变引用计数,但是block内部对引入的外部对象,会更改引用计数。所以要及时对block进行release.block_release(block)

那上面间接的可以解释为什么有__forwarding指针,而不是age直接指向age

(age->__forwarding->age) = 20;

因为这个__Block_byref_age_0对象刚开始都是在栈上,当block被copy到堆上的时候,自动也就copy了一份__Block_byref_age_0对象,这个时候,第一个age是栈上的,而它的__forwarding指针是指向堆上的它自己,所以需要用__forwarding的age来取值或者赋值,但是如果block是在栈上,那么这些都不会被copy到堆上,所以__forwarding还是指向自己,所以,这不论是在栈还是再堆都实现了指向自己,所以可以赋值或者取值。(copy到堆上了,地址肯定就发生变化了。)

————————————————
版权声明:本文为CSDN博主「海的天空1661」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_27909209/article/details/81514891

一个栈上 一个堆上 改一个两个都改了能证明么?block地址没变
__block UILabel* tempLabel = [[UILabel alloc]init];
__weak MyBlock myBlock;
myBlock = ^(NSString *str){
tempLabel = [[UILabel alloc]init];
tempLabel.text = str;
NSLog(@"%p",tempLabel);
};

MyBlock strongBlock = [myBlock copy];

strongBlock(@"123");
myBlock(@"456");

但是当外部引用的指针是一个弱指针,那么它还是NSConcreteStackBlock 类型

如果block中没有引用外部变量,那么就会直接是个NSConcreteGlobalBlock 而不论外部引用的指针是不是强引用

在mrc下
由于手写内存管理,release retain, 所以强引用并没有参与指针的copy行为。
在mrc下 只有当block 引用了自动变量,并且对该block使用了 Block_copy() 这个block 才是NSConcreteMallocBlock 类型
如果引用了自动变量而没有copy行为,那么是NSConcreteStackBlock类型

所谓持有
arc下持有block是用一个强指针引用block,mrc下持有block是对block 使用 block_copy。 除此之外,block不会存在堆区
//arc弱引用stackblock 是赋值不是持有 mrc不copy也是赋值没有持有

mrc
copy 直接对 block 使用 Block_copy 行为 , 也是系统推荐的 方式。
retain block 依旧保持原来类型, 引用计数不会增加
assign 依旧保持原来类型,引用计数不会增加
strong (mrc 下 可以用 strong 了, 是不是也就说 如果都是用 strong 那么mrc 和 arc 没区别了? 应该不是,因为arc mrc 是编译选项,编译时期只不过做了 添加 release retain 的操作,如果不 打开 arc, 那么就要自己添加了 retain release 了), 和copy 行为一样了,把block 放到堆内存了 , 并且block 类型也变成 malloc block 了。(对啊为什么大家默认MRC不能使用weak和strong?又不是废弃了MRC后出的)

arc 下
copy 和 strong 行为一致
因为block 是不可变对象,所以都只是增加了一个强引用而已,arc 下 block 只要有强引用并且内部引用了自动变量,他就在堆区。
weak 依旧保持block 原样, 没有copy行为。

一些细节

作者:zx1798
链接:https://www.jianshu.com/p/e713d5ab5627
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


##还有一个问题 arc和mrc对NSStackBlock转换的c++几乎没变化怎么进行的内存管理 会导致person过{}一个释放一个没释放(arc被copy了所以没释放?mrc呢?)?

mrc 是赋值? UILabel tempLabel; 栈block 双方在同一个调用栈不会野指针 过了调用栈都释放。。。 就不强引用了
struct __Block_byref_tempLabel_0 {
void __isa;
__Block_byref_tempLabel_0 __forwarding;
int __flags;
int __size;
void (
__Block_byref_id_object_copy)(void
, void
);
void (__Block_byref_id_object_dispose)(void);
UILabel *tempLabel;
};

UILabel* tempLabel = [[UILabel alloc]init];
__weak void (^MyBlock)(void);
MyBlock = ^{
NSLog(@"%p",&tempLabel);
};
MyBlock();
(lldb) po [MyBlock class]
NSStackBlock


arc解决循环引用   __weak  __unsafe_unretain  __block(block要被调用,并置位nil)
mrc   __unsafe_unretain  __block(不会将栈block复制到堆上)
当栈空间的block在进行copy操作变成堆空间的block时,block内部会自动调用__main_block_copy_0函数,我们看看block函数的内部:

static void __main_block_copy_0(struct __main_block_impl_0dst, struct __main_block_impl_0src)

{_Block_object_assign((void)&dst->person, (void)src->person, 3/BLOCK_FIELD_IS_OBJECT/);

}
在__main_block_copy_0内部会调用_Block_object_assign函数,并且把person当做参数传递进去.而在_Block_object_assign函数内部,会根据auto变量的修饰符(__strong,__weak,__unretained)进行相应的操作,形成强引用(retain)或者弱引用

作者:韩大叔
链接:https://www.jianshu.com/p/86fbe9dfb0f5
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
熟悉的isa指针

Variable is not assignable (missing __block type specifier)
block外部的局部变量a和block内部的局部变量a不是同一个变量  外部的a不能跨作用域 外部的a在栈上  内部的a随block被复制到堆上  只是名字相同  内部的a只是一个瞬时值 因为和外部a同名 在编译器层面进行了禁止更改的限制 __cself->tempLabel 访问的是内部被复制到堆上的a




1:为什么block会捕获自动变量的瞬时值?为何如此设计?
2:为什么block内部要加__strong,__strong为什么不会重新引起循环引用?__weak为什么能解决循环引用?
block在被定义后会转为如下的结构体,
#对于自动变量来说
//结构体内部有新的指针UILabel *__strong tempLabel,和原来的tempLabel指向同一块内存地址但是是不同的两个指针,UILabel *__strong tempLabel = __cself->tempLabel 使用的是block结构体内的指针,外部更改指针指向不会影响block内部,所以实现了捕获变量的瞬时值,不会跟踪外部局部变量更改
//其实捕获瞬时值是默认实现,加上__block可以跟踪外部变化 ;  apple考虑的还是比较全面的  既可以跟踪外部变化也可以不跟踪外部变化
//  UILabel *__strong tempLabel; 同样也是强引用导致循环引用的原因  如果给外部加上__weak,则__main_block_impl_0中的__strong会替换为__weak从而解决循环引用; 但是函数__main_block_func_0中的 UILabel *__strong tempLabel也会变成__weak  如果tempLabel被提前释放会导致__main_block_func_0中的被置位nil引发异常  所以在函数内加上__strong(函数内的strong weak好像不对循环引用有影响)

UILabel *tempLabel;

//block结构体
struct __main_block_impl_0 {
struct __block_impl impl;//isa flags reserved FuncPtr
struct __main_block_desc_0* Desc;
UILabel *__strong tempLabel;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, UILabel *__strong _tempLabel, int flags=0) : tempLabel(_tempLabel) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block方法实现
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
UILabel *__strong tempLabel = __cself->tempLabel; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_91_cnr24zps6l5bw9nt_djyzdd80000gn_T_main_5c278d_mi_0,tempLabel);
}

//copy方法
static void __main_block_copy_0(struct __main_block_impl_0dst, struct __main_block_impl_0src) {_Block_object_assign((void)&dst->tempLabel, (void)src->tempLabel, 3/BLOCK_FIELD_IS_OBJECT/);}

//dispose方法
static void __main_block_dispose_0(struct __main_block_impl_0src) {_Block_object_dispose((void)src->tempLabel, 3/BLOCK_FIELD_IS_OBJECT/);}
//内存管理
static struct __main_block_desc_0 {
size_t reserved; //保留字段?
size_t Block_size; //block大小
void (copy)(struct __main_block_impl_0, struct __main_block_impl_0);
void (
dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {

UILabel* tempLabel = ((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UILabel"), sel_registerName("alloc")), sel_registerName("init"));
void (*MyBlock)(void);
MyBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, tempLabel, 570425344));
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
变量释放

// __weak UIButton *btn2 = btn;
// __strong UIButton btn3 = btn2;
// printf("retain count =%ld\n",CFGetRetainCount((__bridge CFTypeRef)(btn)));
//
__weak typeof(self) weakSelf = self;
self.testBlock = ^{
__strong ViewController2
strongSelf = weakSelf;
NSLog(@"%@",strongSelf);

};
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
           self.testBlock();
       });

}


1:为什么要捕获局部变量 会野指针等等
    不捕获局部就过去作用域
2:为什么是瞬时值    如果不是捕获瞬时值 而是捕获指针 该指针如果是局部变量 函数结束指向的内容被释放  该指针会导致野指针吧?可能是基于这个原因 所以static变量 会以指针的指针传递?static int uilabel是多了个*转为指针UILabel *__strong *tempLabel(再说等用到的时候有可能已经出局部变量的上下文作用域了改了也没用?自说自话)
3:代码上怎么实现捕获瞬时值

//    局部的int UILabel 捕获瞬时值      block内部有个新的指针 指向原本值的内存地址   被捕获时作为一个struct的成员变量
//    全局的int UILabel是靠作用域范围
//    static int uilabel是多接了个*转为指针UILabel *__strong *tempLabel 指针地址变了 指向地址没变  类似于nsstring更改值
//    __block修饰的int uilabel  转成了包含isa的结构体(对象)__Block_byref_tempLabel_0

strong和copy在arc都可以修饰block  内部实现都是
id objc_retainBlock(id x) {
    return (id)_Block_copy(x);
}
为什么能访问外部变量,就是因为将外部变量复制到了结构体中,即自动变量会作为成员变量追加到 Block 结构体中。

通常默认强引用 改成弱引用后会出现以下更改
  UILabel *strong AAAA; 是强引用循环引用的根本原因

__main_block_impl_0中的 UILabel *__weak AAAA;是导致强弱引用的根本原因
UILabel *__weak AAAA = __cself->AAAA; // bound by copy
错误理解<此处已经为方法实现 内部block执行完毕 会进行引用释放无所谓强弱 所以添加加strongSelf也不会导致循环引用>
因为用weak修饰 当AAAA释放时会导致block结构体内weak AAAA指针被置位nil,用strong强引用一下避免这种情况发生。而strongSelf是block func内局部变量 会随 func作用域出栈 即底层block的结构体实现和func是剥离开的开的


UILabel *tempLabel;

struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
此处由strong->__weak
UILabel *__weak AAAA;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, UILabel *__weak _AAAA, int flags=0) : AAAA(_AAAA) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
此处由__weak修饰
UILabel *__weak AAAA = __cself->AAAA; // bound by copy

    __attribute__((objc_ownership(strong))) UILabel *strongLable = AAAA;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_91_cnr24zps6l5bw9nt_djyzdd80000gn_T_main_ebc462_mi_0,AAAA);
}

static void __main_block_copy_0(struct __main_block_impl_0dst, struct __main_block_impl_0src) {_Block_object_assign((void)&dst->AAAA, (void)src->AAAA, 3/BLOCK_FIELD_IS_OBJECT/);}

static void __main_block_dispose_0(struct __main_block_impl_0src) {_Block_object_dispose((void)src->AAAA, 3/BLOCK_FIELD_IS_OBJECT/);}

static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (copy)(struct __main_block_impl_0, struct __main_block_impl_0);
void (
dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {

UILabel* tempLabel = ((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UILabel"), sel_registerName("alloc")), sel_registerName("init"));
__attribute__((objc_ownership(weak))) typeof(tempLabel) AAAA = tempLabel;
void (*MyBlock)(void);
MyBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, AAAA, 570425344));
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_91_cnr24zps6l5bw9nt_djyzdd80000gn_T_main_ebc462_mi_1,tempLabel);
NSString * appDelegateClassName;
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

    appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);

}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };




从栈上复制到堆上
stack malloc global

struct __Block_byref_tempLabel_0 {
void __isa;
__Block_byref_tempLabel_0 __forwarding;
int __flags;
int __size;
void (
__Block_byref_id_object_copy)(void
, void);
void (
__Block_byref_id_object_dispose)(void*);
UILabel *__strong tempLabel;
};

struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_tempLabel_0 *tempLabel; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_tempLabel_0 *_tempLabel, int flags=0) : tempLabel(_tempLabel->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_tempLabel_0 *tempLabel = __cself->tempLabel; // bound by ref

         NSLog((NSString *)&__NSConstantStringImpl__var_folders_91_cnr24zps6l5bw9nt_djyzdd80000gn_T_main_7b87d7_mi_0,(tempLabel->__forwarding->tempLabel));
         (tempLabel->__forwarding->tempLabel) = ((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UILabel"), sel_registerName("alloc")), sel_registerName("init"));
     }
 static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->tempLabel, (void*)src->tempLabel, 8/*BLOCK_FIELD_IS_BYREF);}

 static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->tempLabel, 8/*BLOCK_FIELD_IS_BYREF);}

 static struct __main_block_desc_0 {
   size_t reserved;
   size_t Block_size;
   void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
   void (*dispose)(struct __main_block_impl_0*);
 } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
 int main(int argc, char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_tempLabel_0 tempLabel = {(void*)0,(__Block_byref_tempLabel_0 *)&tempLabel, 33554432, sizeof(__Block_byref_tempLabel_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)((UILabel *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UILabel"), sel_registerName("alloc")), sel_registerName("init"))};
     void (*MyBlock)(void);
     MyBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_tempLabel_0 *)&tempLabel, 570425344));
     ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);

*/

上一篇下一篇

猜你喜欢

热点阅读