block在美团iOS的实践

说到block,相信大部分iOS开发者都会想到retain cycle或是__block修饰的变量。

但是本文将忽略这些老生常谈的讨论,而是将重点放在美团iOS在实践中对block的应用,希望能对同行有所助益。

本文假设读者对block有一定的了解。

从闭包说起

在Lisp这样的语言中,有一个概念叫做闭包(closure1),指的是一个函数以及它所处的词法作用域(lexical scope2)构成的整体。为了理解闭包,我们首先来看看什么是词法作用域。

所谓词法作用域,顾名思义,是指一个符号引用的是其词法环境中的变量,而无关程序在运行时的状态。这么说可能有点抽象,让我来看一段Common Lisp3代码:

(defvar printer (let ((x 42))(lambda () (format t "~a" x))))

这里我们定义了一个变量printer,它的值是一个函数,这个函数会打印词法作用域中的变量x(其值为42)。

现在我们来调用这个函数:

CL-USER> (funcall printer)
42

可以看到,我们调用了printer中存放的函数之后,打印出来的数字是42,跟我们的预期相符。

接下来再让我们看一个可能会出乎意料的结果:

CL-USER> (let ((x 1))(funcall printer))
42

我们在调用之前把x设置为了1,但是打印的结果仍然是42。

为什么?因为printer中存放的函数在被调用时所引用的变量位于其词法作用域中, 即该函数被定义时所处的词法环境中,所以程序在运行时设置的变量x对函数不起作用。

前面我们讲过,所谓闭包,就是函数及其词法作用域的合称,具体到上例,那么匿名函数和x就构成了一个闭包,它会为函数保存一种状态,有点类似于全局变量,不过除了那个匿名函数,其他函数无法访问到x。

说了这么多,似乎跟block毫无关系?事实上,block为C带来了闭包。

Block

Apple从OS X 10.6和iOS 4以后开始支持block,让我们用C把上面的例子重写一下:

#include <stdio.h>int main ()
{int x = 42;void (^block)() = ^() {printf("%d\n", x);};block();x = 1;block();return 0;
}

编译运行后得到的输出同样是两个42。

到了这里,相信读者对闭包已经有一个直观的认识了,但是它有什么用?有什么好处?

设想如下场景,我们要请求一个URL,并以block的形式传入回调函数,并在回调函数中用到刚才这个URL:

NSURL *someURL = …;
[SomeClass getURL:someURL finished:^(id responseObject) {// process responseObject with someURL
}];

这里网络请求是异步的,所以当block中代码执行时,getURL:finished:方法调用所在的栈很可能已经不存在了,但是因为回调block和someURL构成了closure,所以即使栈不存在,block仍然可以引用到someURL。

可能你会说,“我在block中增加一个NSURL类型的参数,把someURL传回来不也可以实现同样的目的吗?”不妨设想如果我们在block中要引用的对象有10个之多,用参数列表传递明显不再现实,用容器类或者专门定义一个类来传递虽然可以,但是前者没有编译器为我们检查错误,后者则相当繁琐。而利用闭包,可以轻易达到灵活性和简洁性的平衡。事实上,美团客户端就大量利用了闭包,在UI层发出请求,在回调中更新某些UI组件。

函数式编程4

在Lisp中,函数是一等公民,可以随时创建、作为参数传递、作为返回值返回,Objective C在没有block之前,没有类似的机制,有了block,Objective C也就具备了函数式编程的能力,block是对象,有自己的ISA指针,可以随时创建,作为参数传递,作为返回值返回。

先来看看block的经典用法:

[UIView animateWithDuration:0.25 animations:^{self.view.alpha = 1.0f;}];

UIView的animateWithDuration:animations:方法的第二个参数是一个block,它把跟动画相关的操作封装起来传递进去,以实现动画效果。

现在让我们发掘一下类似的用法:

[SAKBaseModel comboRequest:^() {[dealModel fetchDealByID:123456withFields:nilcompletion:^(MTDeal *deal, NSError *error) {...}];[orderModel fetchOrderByID:654321withDealFields:nilcompletion:^(MTOrder *order, NSError *error) {...}];
}];

这里我们为SAKBaseModel设计了一个类似于UIView的接口叫comboRequest,它会接受一个block作为参数,在这个block中发出的请求都会作为combo请求的一部分。如果dealModel或者orderModel的任何一个请求不是出现在block中,那么它就是一个普通的请求。这样做的好处是dealModel和orderModel的接口不需要关心自己是不是属于一个combo请求,调用者则可以灵活地调整代码。

那么怎么实现这样的接口呢?还是从UIView上获取灵感。我们知道UIView有个方法setAnimationsEnabled:,实际上SAKBaseModel也可以有这么一个方法:setComboRequestEnabled:,而在comboRequest方法的实现中,在调用传进来的block之前先setComboRequestEnabled:YES,调用完后再恢复为原状态。相应的,在实际的model接口中,检查comboRequest是否为YES,如果是,则把自己作为一个combo请求的一部分,否则正常发出请求即可。

Think Big

Lisp最强大的特性之一是condition系统,它可以分离异常的检测、异常的解决和异常解决方式的决策,看一段示例代码:

(define-condition network-timeout-error (error)((url :initarg :url :accessor url)))(defun try-again (condition)(let ((restart (find-restart ‘try-again)))(when restart (invoke-restart restart))))(defun deal-requester (deal-id)(handler-bind ((network-timeout-error #’try-again))(request-from-url (format nil “http://api.mobile.meituan.com/deal/~a” deal-id)(lambda (deal error)(if error(format t “error: ~a”, error)(process-deal))))))
(defun request-from-url (url finished)(let ((callback (lambda (response error)(if (network-timeout-error-p error)(error ‘network-timeout-error :url url)(funcall finished (parse-deal response) error)))))(restart-bind((try-again (lambda () (http-request url callback))))(http-request url callback))))

可以看到,condition系统对于代码的分层提供了良好的支持,请求超时的错误在底层代码被检测到,在发出请求前注册一个restart,而在业务层去决定要不要调用restart。

一直以来,C语言要实现优雅的异常处理就是一件不简单的事情,而Objective-C虽然加入了try-catch支持,但是苹果并不鼓励使用,那么能否实现类似于condition系统这样的异常处理机制呢?

答案是能。让我们来看看接口设计:

typedef void (^RESTART)(id userInfo);
typedef void (^HANDLER)(id condition);void restart_bind(void (^body)(), NSString *restartName, RESTART restart, ...) NS_REQUIRES_NIL_TERMINATION;void handler_bind(void (^body)(), Class class, HANDLER handler, ...) NS_REQUIRES_NIL_TERMINATION;void notify(id condition);RESTART find_restart(NSString *restartName);

如下图所示,handler_bind首先在栈中注册好handler,而restart_bind则在handler有效的环境中注册restart,当有异常发生时,notify函数会在当前环境中寻找handler,找到后,控制会转移到上层的handler代码中,这时handler可以用find_restart在栈中搜索restart,找到之后可以调用,从而实现异常的恢复,做完这一切,控制回到notify发生的点继续向下执行。

完整的代码敬请期待美团iOS的开源项目。

有了SAKCondition,我们可以实现任意底层代码的逻辑穿透到上层代码,比如网络层和UI层,使得上层代码可以在不了解下层代码实现细节的情况下调用恢复机制。事实上,美团的iPhone客户端就是利用SAKCondition实现了美团账户的安全解锁功能。

总结

block给Objective C带来了无穷的可能性。本文只讨论了美团iOS在实践中的一些用法,更多想法还在等待挖掘。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/477864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

写Rap,编菜谱,你画我猜……这些 AI demo 我可以玩一天!

文 | ZenMoore编 | 小轶上次写的那篇 《Prompt 综述15篇最新论文梳理]》 有亿点点肝。这次给大家整点轻松好玩的&#xff08;顺便给这篇推文打个广告&#xff0c;快去看&#xff01;&#xff09;。不知道读者朋友们有没有遇到这样的情况&#xff1a;有新的论文发表了&#xff0…

LeetCode 1254. 统计封闭岛屿的数目(图的BFS DFS)

文章目录1. 题目2. 解题2.1 DFS2.2 BFS1. 题目 有一个二维矩阵 grid &#xff0c;每个位置要么是陆地&#xff08;记号为 0 &#xff09;要么是水域&#xff08;记号为 1 &#xff09;。 我们从一块陆地出发&#xff0c;每次可以往上下左右 4 个方向相邻区域走&#xff0c;能…

技术动态 | 图对比学习的最新进展

转载公众号 | DataFunSummit 分享嘉宾&#xff1a;朱彦樵 中国科学院自动化研究所编辑整理&#xff1a;吴祺尧 加州大学圣地亚哥分校出品平台&#xff1a;DataFunSummit导读&#xff1a;本文跟大家分享下图自监督学习中最近比较热门的研究方向&#xff1a;图对比学习&#xff0…

如何把Android手机变成一个WIFI下载热点? — 报文转发及DNS报文拦截

随着WiFi的普及&#xff0c;移动运营商的热点也越来越多了&#xff0c;如中国移动的CMCC、中国电信的ChinaNet、中国联通的ChinaUnicom等&#xff0c;一般来说&#xff0c;连上此类的热点&#xff0c;打开浏览器上网时都会自动跳转到一个验证页面&#xff0c;最近有个项目也有类…

OpenKG 祝大家新春快乐

OpenKG 祝各位读者新春快乐&#xff0c;虎虎生威&#xff01;OpenKGOpenKG&#xff08;中文开放知识图谱&#xff09;旨在推动以中文为核心的知识图谱数据的开放、互联及众包&#xff0c;并促进知识图谱算法、工具及平台的开源开放。点击阅读原文&#xff0c;进入 OpenKG 网站。…

推荐中使用FNN/PNN/ONN/NFM优化特征交叉

文 | 水哥源 | 知乎sayingDNN时代来临的时候&#xff0c;LR没打过&#xff0c;也很难加入。FM打不过&#xff0c;但他可以加入FM的精髓&#xff0c;最上在于latent embedding&#xff0c;有了它才能把交互拆解到基底上&#xff1b;居中在于element-wise乘&#xff0c;能让两个特…

LeetCode 310. 最小高度树(图 聪明的BFS,从外向内包围)

文章目录1. 题目2. 解题2.1 暴力BFS2.2 聪明的BFS1. 题目 对于一个具有树特征的无向图&#xff0c;我们可选择任何一个节点作为根。图因此可以成为树&#xff0c;在所有可能的树中&#xff0c;具有最小高度的树被称为最小高度树。给出这样的一个图&#xff0c;写出一个函数找到…

Emma使用与分析

什么是Emma EMMA 是一个开源、面向 Java 程序测试覆盖率收集和报告工具。 它通过对编译后的 Java 字节码文件进行插装&#xff0c;在测试执行过程中收集覆盖率信息&#xff0c;并通过支持多种报表格式对覆盖率结果进行展示。 EMMA 所使用的字节码插装不仅保证 EMMA 不会给源代码…

论文浅尝 | WWW2022 - “知识提示”之知识约束预训练微调

本文作者 | 陈想&#xff08;浙江大学&#xff09;、张宁豫&#xff08;浙江大学&#xff09;、谢辛&#xff08;陈想&#xff09;、邓淑敏&#xff08;浙江大学&#xff09;姚云志&#xff08;浙江大学&#xff09;、谭传奇&#xff08;阿里巴巴&#xff09;&#xff0c;黄非&…

吐血整理:论文写作中注意这些细节,能显著提升成稿质量

文 | python编 | 小轶前言不知诸位在科研的起步阶段&#xff0c;是否曾有过如下的感受&#xff1a;总感觉自己写的论文就是和自己读过的论文长得不太一样&#xff0c;也不知道为啥。投稿的时候&#xff0c;审稿人也总是 get 不到论文的核心&#xff0c;只揪着论文的次要细节不放…

LeetCode 1237. 找出给定方程的正整数解

1. 题目 给出一个函数 f(x, y) 和一个目标结果 z&#xff0c;请你计算方程 f(x,y) z 所有可能的正整数 数对 x 和 y。 给定函数是严格单调的&#xff0c;也就是说&#xff1a; f(x, y) < f(x 1, y) f(x, y) < f(x, y 1)函数接口定义如下&#xff1a; interface Cu…

基于Wi-Fi的室内定位在美团总部的实践和应用(上)

室内定位技术的商业化必将带来一波创新高潮&#xff0c;尤其是在O2O领域&#xff0c;各种基于此技术的应用将出现在我们的面前。我们可以想象一些比较常见的应用场景&#xff0c;比如在大型商场里面借助室内导航快速找到目标商铺&#xff0c;商店根据用户的具体位置向用户推送更…

LeetCode 1266. 访问所有点的最小时间(数学)

1. 题目 平面上有 n 个点&#xff0c;点的位置用整数坐标表示 points[i] [xi, yi]。请你计算访问所有这些点需要的最小时间&#xff08;以秒为单位&#xff09;。 你可以按照下面的规则在平面上移动&#xff1a; 每一秒沿水平或者竖直方向移动一个单位长度&#xff0c;或者…

Coding Party 邀你出战!飞桨黑客马拉松线下场来啦

12月4-5日&#xff0c;这48H&#xff0c;与100开发者HACK Together“黑客”很酷&#xff1f;和“黑客”一起“战斗”是种什么样的体验&#xff1f;你参与这场“战斗”了吗&#xff1f;PaddlePaddle Hackathon 2021飞桨黑客马拉松&#xff0c;由百度飞桨联合深度学习技术及应用国…

技术动态 | 「知识图谱嵌入技术研究」最新2022综述

转载公众号 | 专知知识图谱(KG)是一种用图模型来描述知识和建模事物之间关联关系的技术. 知识图谱嵌入(KGE)作为一 种被广泛采用的知识表示方法,其主要思想是将知识图谱中的实体和关系嵌入到连续的向量空间中,用来简化操作, 同时保留 KG 的固有结构.它可以使得多种下游任务受益…

全球仅3000人通过的TensorFlow开发人员认证到底有多香!

现在&#xff0c;我们每个人都已经习惯了社交网络&#xff0c;随时随地都能进行微信、QQ语音和视频通话。那么&#xff0c;你是否发现在不同场景下&#xff0c;这些社交软件音频、视频都是那么的流畅&#xff0c;这背后是什么科技在支撑这“声”与“话”的美好呢&#xff1f;这…

LeetCode 1043. 分隔数组以得到最大和(DP)

1. 题目 给出整数数组 A&#xff0c;将该数组分隔为长度最多为 K 的几个&#xff08;连续&#xff09;子数组。分隔完成后&#xff0c;每个子数组的中的值都会变为该子数组中的最大值。 返回给定数组完成分隔后的最大和。 示例&#xff1a; 输入&#xff1a;A [1,15,7,9,2,…

图谱实战 | 安全领域知识图谱建设与典型应用场景总结

转载公众号 | 老刘说NLP 安全知识图谱是网络安全领域专用知识图谱&#xff0c;也是知识图谱应用于安全业务的重要工业尝试。当前&#xff0c;安全领域中存在大量的业务数据&#xff0c;建模需求以及应用需求&#xff0c;了解安全领域知识图谱的建设方法以及典型应用场景&#x…

Hadoop安全实践

前言 在2014年初&#xff0c;我们将线上使用的 Hadoop 1.0 集群切换到 Hadoop 2.2.0 稳定版&#xff0c; 与此同时部署了 Hadoop 的安全认证。本文主要介绍在 Hadoop 2.2.0 上部署安全认证的方案调研实施以及相应的解决方法。 背景 集群安全措施相对薄弱 最早部署Hadoop集群时并…

论文浅尝 | KGQR: 用于交互式推荐的知识图谱增强Q-learning框架

笔记整理&#xff1a;李爽&#xff0c;天津大学链接&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3397271.3401174动机交互式推荐系统(IRS)以其灵活的推荐策略和考虑最佳的长期用户体验而备受关注。为了处理动态用户偏好&#xff0c;研究人员将强化学习(reinforcement lear…