Block使用方法总结

- block 本质

block 本质上也是一个OC对象,它内部也有一个 isa 指针
block是封装了函数调用以及函数调用环境的OC对象

- block 内调用局部变量和全局变量的原理:

1、block内调用局部变量时:会把局部变量捕获到block内,也就是block内会创建一个对象来接收局部变量,比如局部变量是int a;那么block内就会也创建一个int a;来接收外面的局部变量,这时接收的是这个局部变量的值,那么后面再在block外面改变这个局部变量的值,block里面的这个局部变量的值也不会变了,因为block里面调用的局部变量a是从内部创建的a里取的值。如果局部变量前面加了一个static修饰,那么这时block里面创建的就是这个局部变量的指针也就是地址用来接收这个局部变量的地址,所以这个时候block里面调用这个局部变量时就是调用的地址,这时后面再修改局部变量的值那么地址里面的值也会变。
2、block内调用全局变量时:不会把全局变量捕获到block内,是直接调用全局变量,所以后面改变全局变量的值那么block里也会改变

成员变量属于局部变量,self属于局部变量
成员变量:@property (nonatomic, copy) NSArray *imagesArray;

@implementation MineVC
int a = 10;//写在这是全局变量
 - (void)text{
int b = 10;//写在这是局部变量 
}
@end

- block的copy

1、在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况:
(1)block作为函数返回值时
(2)将block赋值给__strong指针时
(3)block作为 Cocoa API 中,方法名里含有usingBlock的方法参数时
(4)block作为 GCD API 的方法参数时
2、MRC下block属性的建议写法
@property(copy,nonatomic)void(^block)(void);
3、ARC下block属性的建议写法
@property(strong,nonatomic)void(^block)(void);
@property(copy,nonatomic)void(^block)(void);

- 对象类型的auto变量

1、当block内部访问了对象类型的auto变量时
(1)如果block是在栈上,将不会对auto变量产生强引用
(2)如果block被拷贝到堆上
1>会调用block内部的copy函数
2>copy函数内部会调用_Block_object_assign函数
3>_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)作出相应的操作,类似于regain(形成强引用、弱引用)
(3)如果block从堆上移除
1>会调用block内部的dispose函数
2>dispose函数内部会调用_Block_object_dispose函数
3>_Block_object_dispose函数会自动释放引用的auto变量,类似于release

typedef void (^MJBlock)(void);
int main(int argc, const char * argv[]) {//主函数
	@autoreleasepool {
		int age = 10;
		MJBlock block = ^{
 			age = 20;//这样写是错误的,因为 int age = 10;这个age是在main函数里面的(内存也就存在main函数这个栈空间里),而block方法其本质是在function这个函数里执行的,你不可能在一个函数里去调用另外一个函数里的局部变量。那么怎么在block里面修改这个age呐,方法一:在int前面加一个 static ,方法二:把age改为全局变量,方法三:在int前面加上 __block ;方法一和方法二block里面修改age时都相当于直接拿到age的指针地址进行修改age,这时当然是可以修改的。方法三编译器会将 __block 变量包装成一个对象,也就是相当于在block里面创建了一个age对象有地址有内存,然后在block里改age就是改的自己创建的这个有地址有内存的age。方法一和方法二相当于把age改成了全局变量那么就会一直存在内存中,即使这个函数执行完了也不能及时释放,而在前面加上 __block 那么age也会在函数结束后及时释放,不会一直存在内存中。
		NSLog (@"age is %d", age);
		}
	}
	return 0;
}
## - 对象类型的auto变量、__block变量

1、当block在栈上时,对他们都不会产生强引用
2、当block拷贝到堆上时,都会通过copy函数来处理他们
(1)__block变量(假设变量名叫 a )
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
(2)对象类型的auto变量(假设变量名叫 p)
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
3、当block从堆上移除时,都会通过dispose函数来释放它们
(1)__block变量(假设变量名叫 a )
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
(2)对象类型的auto变量(假设变量名叫 p)
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
BLOCK_FIELD_IS_OBJECT:表示对象
BLOCK_FIELD_IS_BYREF:表示__block变量

- 被__block修饰的对象类型

1、当__block变量在栈上时,不会对指向的对象产生强引用
2、当__block变量被copy到堆时
(1)会调用__block变量内部的copy函数
(2)copy函数内部会调用_Block_object_assign函数
(3)_Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)作出相应操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
3、如果__block变量从堆上移除
(1)会调用__block变量内部的dispose函数
(2)dispose函数内部会调用_Block_object_dispose函数
(3)_Block_object_dispose函数会自动释放指向的对象(release)

- 解决block循环引用问题 - ARC

方法一:用__weak、__unsafe_unretained 解决

__weak thpeof(self) weakSelf = self;
self.block = ^{
    printf("%p", weakSelf);
};

__unsafe_unretained id weakSelf = self;
self.block = ^{
    NSLog(@"%p", weakSelf);
}

方法二:用__block解决(必须要调用block)

__block id weakSelf = self;
self.block = ^{
    printf("%p", weakSelf);
    weakSelf = nil;
};
self.block();

猜你喜欢

转载自blog.csdn.net/weixin_39487291/article/details/88547266
今日推荐