Objective-C block、weakself、strongself
一、前言
Block
是OC语言中的一种数据类型,它是预先准备好的代码,在需要的时候可以直接执行。可以做为参数传递,也可以做为返回值,一般在实际开发中前者用的比较多。在多线程和网络场景中使用得也相当频繁,一般都是作为回调使用!其形式类似于C语言中指向函数的指针,在开发中也是反向传值的一种方式。
在OC中使用^
操作符声明一个block
变量,^
也是标示着一段block
代码块的开始。block
的实体包含在{}
中。
比如作为参数传递:
NSURL *url = [NSURL URLWithString:alertView.message];
[IJKVideoViewController presentFromViewController:self withTitle:[NSString stringWithFormat:@"URL: %@", url] URL:url completion:^{
[self.navigationController popViewControllerAnimated:NO];
}];
二、Block 基本用法
Block常见的4种形式如下:
-
无返回值,无参数:
void(^myBlock)() = ^{};
-
无返回值,有参数:
void(^myBlock)(int ,int) =^(int ,int ) {};
-
有返回值,无参数:
int(^myBlock)()= ^{};
-
有返回值,有参数:
int (^myBlock)(int,int) = ^(int,int){};
Block代码块不好记,苹果提供快捷键在实现文件的方法体中敲 inl
直接回车,就可以出现一个block
的基本块。
语法如下:
returnType(^blockName)(parameterTypes) = ^(parameters) {
statements
}
在使用block
时会写出如下代码:
- (void)function{
__weak typeof(self) weakself = self; //创建一个指向当前对象的弱引用
[teseObject callFunc:^{
__strong typeof(self) strongself = weakself; //block内部定义一个指向当前对象的强引用
[strongself callFunc_1:....];
[strongself callFunc_2:....];
....
}];
}
针对上面block
块代码,
-
可不可以直接使用
self
?
不可以。因为这样block
会强持有self
对象,造成循环引用,从而导致内存泄露。 -
可不可以直接使用
weakself
?
看情况。由于weakself
不会持有对象,因此不会造成循环引用的问题,但是使用weakself却会造成block
执行不一致问题,试想一下上面的代码,当调用“callFunc_1”的时候weakself是有效的,但是当调用“callFunc_2”的时候weakself
可能已经是nil
了,这样就造成了block内执行不一致从而导致意想不到的结果。 -
循环引用是不是都是问题?
答案是否定的。当block
开始执行的时候,strongself
会去取self
对象的值,如果此时self
已经为nil
,那么整个block
执行期间strongself
都是nil
,如果self
有效,那么strongself
就是利用了循环引用的特性保证了在block
执行的时候self
对象不会被析构(内存管理),保证block
执行的一致性。其实我们在编写业务代码的时候(很多第三方开源类库)中会利用到循环引用的这一特性来保证block
中引用的对象在block
执行的时候依然有效,但是切忌使用黑魔法的时候要在block
执行结束后打破循环引用。由于strongself
是block
内部定义的变量,在block
执行结束会由系统回收从而打破循环引用。
三、weakself 与 strongself
在OC的block
中如果使用self
的话会引起循环引用,也就是说,在block
中直接使用self
的话会被强引用,解决方案是使用#define WEAKSELF typeof(self) __weak weakSelf = self;
,这样在block
结束释放的时候,weakself
因为是弱引用,也会被释放掉。一般情况下是没问题,但是,以下情况会有例外:
WeakSelf(weakSelf)
[self myBlock:^(NSString *str) {
/**
* 第一次调用的时候weakSelf还没有被释放
*/
[weakSelf doSomething];
/**
* 第二次调用的时候weakSelf有可能就被释放掉了
*/
[weakSelf doOtherThing];
}];
为解决weakself
被释放的问题,这个时候就要用到strongSelf
,如下
WeakSelf(weakSelf)
[self myBlock:^(NSString *str) {
/**
* 在block中需要多次使用weakself时要转成strongSelf,这样确保在block执行完成之前不被释放掉
*/
__strong __typeof(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doOtherThing];
}];
对于经常使用Block传值的应用场景,为防止循环引用可通过定义宏变量方式应用WEAKSELF
、STRONGSELF
:
#define WEAKSELF typeof(self) __weak weakSelf = self;
#define STRONGSELF typeof(self) __strong strongSelf = self;
使用时直接在要在使用地方应用即可:
- (void)checkPermissionWithCompletion:(void (^)(BOOL isRecordable, NSString *recordingUrl, NSString *errorInfo))handler {
WEAKSELF
[[NemoSDK sharedInstance] checkPermissionWithMeetingNumber:self.meetingNumber completion:^(BOOL isRecordable, NSString *recordingUrl, NSString *errorInfo) {
weakSelf.recordingUrl = recordingUrl;
handler(isRecordable,recordingUrl, errorInfo);
}];
}
四、实战讲解
// 初始化网关
__weak ViewController *weakSelf = self;
[self initialGateway:^{
__strong ViewController *strongSelf = weakSelf;
[strongSelf gotoH5View];
}];
# pragma mark - 初始化网关
// completeHandler 为块代码,可控制其在initialGateway中的执行位置
- (void)initialGateway:(void (^)(void))completeHandler
{
[[EMMProxyUtils sharedInstance] startProxyServerWithCompletion:^(NSError *error) {
if (error) {
// 初始化网关失败
[self showAlert:@"初始化网关失败." handler:^(UIAlertAction *action) {
[self initialGateway:completeHandler];
} cancelHandler:^(UIAlertAction *action) {
exit(0);
}];
} else {
// 本地代理服务启动成功
NSUInteger proxyServerPort = [[EMMProxyUtils sharedInstance] portForBusinessServerHost:kTargetHost port:kTargetPort];
if (proxyServerPort == 0) {
// 获取本地代理端口失败.
[self showAlert:@"获取本地代理端口失败." handler:^(UIAlertAction *action) {
[self initialGateway:completeHandler];
} cancelHandler:^(UIAlertAction *action) {
exit(0);
}];
return;
} else {
completeHandler();
}
}
}];
}
- (void)gotoH5View {
[BPGeneralContainerSDK initSDKWithPluginXMLPath:[[NSBundle mainBundle] pathForResource:@"DemoCordovaConfig" ofType:@"xml"]];
DemoConfig *config = [[DemoConfig alloc] init];
self.webPageVC = [BPGeneralContainerSDK controllerWithConfigModel:config];
self.webPageVC.bpWKWebviewStatusBlock = ^void(BPWKWebviewStatusType type, WKNavigation *navigation, NSError *error){
NSLog(@"----type ----- %ld", type);
if(type == BPWKWebviewDidFinishNavigation) {
}
};
[self.navigationController pushViewController:self.webPageVC animated:YES];
self.navigationController.navigationBarHidden = YES;
// self.navigationController.interactivePopGestureRecognizer.delegate = self;
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
NSLog(@"-------shq5785-------");
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
UIScreenEdgePanGestureRecognizer *edgeGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeGesture:)];
edgeGesture.edges = UIRectEdgeLeft;
edgeGesture.delegate = self;
[self.webPageVC.view addGestureRecognizer:edgeGesture];
}
五、拓展阅读
- 《iOS学习专栏》
- 点赞
- 收藏
- 关注作者
评论(0)