block是封装了函数调用和函数调用环境的oc对象
(一)block的三种形式
block有三种形式,全局block、栈block、堆block
block类型 | 调用环境 |
---|---|
NSGlobalBlock | 没有访问auto变量 |
NSStackBlock | 访问auto变量 |
NSMallocBlock | stackBlock调用copy |
每种类型block调用copy的结果
block类型 | 执行copy操作 |
---|---|
NSGlobalBlock | 没有变化 |
NSStackBlock | 从栈区拷贝到兑取 |
NSMallocBlock | 引用计数增加 |
(1)全局block,不使用外部变量的是全局block
NSString *str = @"123";
void (^block)(void) = ^{
};
block();
NSLog(@"=======%@", block);
打印结果
2020-09-13 16:59:41.768259+0800 面试[4914:234355] =======<__NSGlobalBlock__: 0x106f0f840>
(2)堆block,使用外部变量的是堆block
NSString *str = @"123";
void (^block)(void) = ^{
NSLog(@"%@", str);
};
block();
NSLog(@"=======%@", block);
打印结果
2020-09-13 17:00:42.136964+0800 面试[4930:235455] 123
2020-09-13 17:00:42.137254+0800 面试[4930:235455] =======<__NSMallocBlock__: 0x600001600090>
(二)block内部使用变量
(1)局部变量---block对局部变量是值捕获
①先修改count的值,再调用block,打印结果仍为未修改的值,可以说明block对局部变量是值捕获
NSInteger count = 123;
void (^block)(void) = ^{
NSLog(@"count=%ld", count);
};
count = 456;
block();
打印结果为
2020-09-14 11:26:51.929949+0800 面试[5623:279738] count=123
NSInteger count = 123;
void (^block)(void) = ^{
count = 456;
};
block();
(2)静态局部变量---block对静态局部变量是指针捕获
static NSInteger count = 123;
void (^block)(void) = ^{
NSLog(@"count=%ld", count);
};
count = 456;
block();
打印结果
2020-09-14 15:10:54.928932+0800 面试[6095:316810] count=456
static NSInteger count = 123;
void (^block)(void) = ^{
count = 789;
NSLog(@"count=%ld", count);
};
count = 456;
block();
打印结果
2020-09-14 15:12:53.073080+0800 面试[6194:319075] count=789
(3)全局变量和静态全局变量-----block不需要捕获,可以直接使用
①
int a = 10;
static int b = 20;
static NSInteger count = 123;
void (^block)(void) = ^{
a = 1000;
b = 2000;
NSLog(@"a=%d b= %d", a, b);
};
a = 100;
b = 200;
block();
打印结果
2020-09-14 15:14:48.444173+0800 面试[6241:321353] a=100 b= 200
②
static NSInteger count = 123;
void (^block)(void) = ^{
NSLog(@"a=%d b= %d", a, b);
};
a = 100;
b = 200;
block();
打印结果
2020-09-14 15:16:06.276051+0800 面试[6262:322567] a=100 b= 200
(三)__block的使用
前面我们说的第一种情况,block使用局部变量,block内部不可以修改局部变量的值,但如果局部变量使用__block修饰,block内部就可以修改局部变量
__block NSInteger count = 123;
void (^block)(void) = ^{
count = 789;
NSLog(@"count=%ld", count);
};
count = 456;
block();
打印结果
2020-09-14 11:29:37.590054+0800 面试[5639:281495] count=789
这是因为用__block修饰的变量count,被block包装成一个结构体,结构体包含count,block内部获取了指向结构体的指针,所以当count改变时,block通过结构体指针找到结构体,继而找到count,因为结构体里的count和外界的count指向的是同一地址。
①
__block NSInteger count = 123;
void (^block)(void) = ^{
count = 789;
NSLog(@"count=%ld", count);
};
count = 456;
block();
NSLog(@"======%ld", (long)count);
打印结果为
2020-09-14 15:49:13.601098+0800 面试[6554:343470] count=789
2020-09-14 15:49:13.601287+0800 面试[6554:343470] ======789
②
__block NSInteger count = 123;
void (^block)(void) = ^{
NSLog(@"count=%ld", count);
};
count = 456;
block();
NSLog(@"======%ld", (long)count);
打印结果
2020-09-14 15:50:46.372492+0800 面试[6579:344931] count=456
2020-09-14 15:50:46.372668+0800 面试[6579:344931] ======456
(四)block为什么用copy修饰
因为使用block内部使用了变量的block是栈block,栈区由系统分配和回收内存,可能我们还需要使用block的时候,block已经被系统回收了,所以为了避免这种情况,我们需要将block从栈区拷贝到堆区,由程序员来管理block。
系统默认情况下会将block从栈区拷贝到堆区的几种情况
- block作为函数返回值
- block为GCD的参数
- block被强引用的情况下
- block作为Cocoa API 方法名中包含using block的方法参数时
比如以下,在block中调用使用self,都不会造成循环饮用:
[UIView animateWithDuration:1 animations:^{
}];
[[NSNotificationCenter defaultCenter] addObserverForName:@"1" object:@"" queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];
dispatch_async(dispatch_get_main_queue(), ^{
});
但是如果,这些方法中,使用了成员变量,block中,再调用self,就会造成循环饮用。如
__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf dismissModalViewControllerAnimated:YES];
}];
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );
(五)block能否改变NSArray
①编译时报错---不能
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
void (^block)(void) = ^{
array = [NSMutableArray array];
};
block();
②可以修改array的属性
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
void (^block)(void) = ^{
[array addObject:@"3"];
NSLog(@"%@", array);
};
[array addObject:@"4"];
block();
打印结果
2020-09-14 15:56:10.471593+0800 面试[6617:348591] (
1,
2,
4,
3
)
block引起的循环引用问题
最近在做类似钉钉打卡的功能,需要检测网络状态,改变UI显示样式,在没有网络的情况下,要显示不能打卡。在有网的情况下,正常显示,
我是用的是AFNetworkReachabilityManager
如下。我在block中使用了self,而不是weakSelf,导致了循环引用,控制器不能释放问题。
- (void)monitorReachabilityStatus {
// 开始监测
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
// 网络状态改变的回调
MJWeakSelf;
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"蜂窝网络");
self.view.backgroundColor = [UIColor redColor];
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WIFI");
self.view.backgroundColor = [UIColor redColor];
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"没有网络");
self.view.backgroundColor = [UIColor redColor];
break;
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知");
self.view.backgroundColor = [UIColor redColor];
break;
default:
break;
}
}];
}
解决方案:只需将self改为weakself即可
- (void)monitorReachabilityStatus {
// 开始监测
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
// 网络状态改变的回调
MJWeakSelf;
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"蜂窝网络");
weakSelf.view.backgroundColor = [UIColor redColor];
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WIFI");
weakSelf.view.backgroundColor = [UIColor redColor];
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"没有网络");
weakSelf.view.backgroundColor = [UIColor redColor];
break;
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知");
weakSelf.view.backgroundColor = [UIColor redColor];
break;
default:
break;
}
}];
}
}
网友评论