Objective-C中logHandler跨类实现调用原理及使用场景问询
问题解答
1. logHandler实现放在另一个类的原因
这是职责分离的常规设计手段:
- LogViewController仅负责自身UI渲染、点击事件的基础响应,不需要耦合具体业务逻辑,后续迭代调整页面UI时不会影响到点击后的业务流程
- LogFlow作为流程控制类统一收拢业务跳转逻辑,业务规则修改时只需要调整Flow层的代码,不需要改动ViewController层的实现
- 大幅提升LogViewController的复用性:如果其他业务场景需要复用该页面,仅需要给logHandler赋值对应场景的业务实现即可,不需要修改ViewController本身的代码。
2. 跨类调用的实现原理
这里用到的是Objective-C的Block(块)语法,本质是可传递的匿名函数:
- LogViewController在头文件声明
dispatch_block_t类型的logHandler属性,相当于对外暴露了一个可赋值的执行入口 - LogFlow在创建LogViewController实例后,给这个入口赋值了具体的执行代码
- 当LogViewController内的按钮被点击时,会先判断
logHandler是否有值,存在的话就执行对应的代码,调用的就是LogFlow中预先赋值的那段逻辑。
3. 不需要导入额外头文件的原因
分两点解释:
- LogFlow侧赋值
logHandler的时候已经导入了LogViewController的头文件,可以正常访问其公开属性 - LogViewController执行
logHandler()的时候,不需要感知Block内部的实现细节:Block内部依赖的类、调用的方法的作用域都在LogFlow侧,编译阶段已经在LogFlow中完成了符号解析,LogViewController只需要执行Block本身,不需要导入LogFlow的相关头文件。
4. 适用场景和正确使用规范
适用场景
- 通用UI组件的回调:比如自定义弹窗、通用列表Cell的点击事件,通过Block把业务逻辑交给上层调用方实现,不需要组件本身耦合业务
- 流程编排解耦:多页面串联的业务流(比如登录、实名认证、提交审核等流程),把页面跳转逻辑收拢在流程控制层,页面只负责触发回调即可
- 异步任务回调:网络请求、本地耗时操作完成后的后续逻辑,也可以通过Block传递实现
使用注意事项
- Block类型的属性必须用
copy修饰:Block默认分配在栈内存,需要copy操作转移到堆内存才能被持有,ARC环境下也需要显式添加copy修饰符 - 避免循环引用:如果Block内部需要引用
self,必须先通过__weak typeof(self) weakSelf = self;生成弱引用,必要时在Block内部用__strong typeof(weakSelf) strongSelf = weakSelf;避免执行过程中实例被提前释放 - 调用Block前必须判空:直接调用未赋值的Block会触发闪退,你当前代码中
if (nil != self.logHandler)的判断是符合规范的 - 复杂回调建议自定义Block类型:如果需要传递参数,不要直接用无参数的
dispatch_block_t,可以自定义类型,比如typedef void(^LoginResultBlock)(BOOL isSuccess, NSString *errorMessage);
内容的提问来源于stack exchange,提问作者stefanosn
相关产品推荐
相关产品推荐

