iOS的动态性来自三个方面:动态类型、动态绑定、动态载入、SEL类型
- 动态类型
Dynamic typing-determining the class of an object at runtime在运行时决定对象类型
1、动态类型<弱类型>(id):在代码的运行阶段判断代码的类型,使用id类型可以让应用在“运行时”使用任何类型来替换。动态类型让程序更加灵活,但是会使数据的统一性降低和代码的可读性。我们常用静态类型<强类型>(如NSString),使用静态类型编译器可以完全分析你的代码,这让代码的性能和可预知性更高。
2、动态绑定:让代码在运行时判断需要调用什么方法,而不是在编译时。动态类型和动态绑定使得选择哪个接收者以及调用什么方法都放到运行时去完成.
3、动态载入:应用程序可以根据需要加载可执行代码以及资源,而不是在启动时就加载所有资源。
4、SEL类型iOS在编译的时候会根据方法的名字(包括参数序列),生成一个用来区分这个方法的唯一的ID,这个ID是SEL类型的,SEL的本质就是类方法的编号[函数地址]。(类似C语言里面的函数指针,但是OC的类不能直接使用函数指针,这样只能做一个@selector语法来取。注意:@selector是查找当前类(含子类)的方法。)
这篇文章不聊具体的技术内容,只是蹭蹭今天 iOS 领域最大的一个热点,关于一些应用收到 Apple 警告的问题。我今天刚起床的时候,iOS 圈就炸锅了,很多开发者纷纷表示收到了 Apple 的警告邮件,邮件内容在网上随处可看,在这里我就不贴了。大体意思是说该应用使用了一些动态技术,让应用在审核之后出现一些不一样的功能,从而越过审核去实现更新,警告开发者对代码进行一些检查,去掉这样的实现。
实际上这是一个历史悠久,错综复杂,没有一个统一标准的事情。聊这个事情要从一个很古老的项目开始(我们直接略过不谈 Hybrid 架构的应用,就谈 Native 动态化)。
# 「始作俑者」Wax
最早要从 Wax 这个项目开始说,大家都知道 Objective-C 有着非常强大的动态特性。比如说:
运行时构造类和方法
运行时替换方法的实现
实际上这两个能力是非常恐怖的,他可以让 Cocoa 项目做很多类似黑魔法的事情,简单说就是可以让 Objective-C 像脚本语言那样,文本即代码,无须编译。后来出现了一个叫做 Wax**的项目(这个项目目前由阿里巴巴维护),这个项目打出的口号是用 Lua 来写 iOS 原生应用,当然现实中没有人会这样干,因为写起来实在是太痛苦了。但是鉴于 iOS 应用审核比写 Wax 还痛苦,所以 Wax 成为了做 HotFix 的最佳选择。
这个项目的做法是通过加载 Lua 脚本,动态的生成 Objective-C 的方法,通常用来替换掉出了问题的那个,Lua 脚本是可以动态下发的,所以也就实现了修复线上 bug 的使命。
当然,Wax 用起来是极为痛苦的,尤其是和 Objective-C 的类型转换。
# 后来居上的 JSPatch
iOS 7 的时候 Apple 推出了 JavaScriptCore,这是一个非常有趣的框架,他是 JS 与原生交互的桥梁,让你在原生和 JS 之间穿梭自如,现在 iOS 平台各种动态技术大多都是基于此。
JSCore 推出不久之后,一个更优秀的项目诞生了:由 bang 写的 JSPatch**。这个项目无疑从各种角度碾压了 Wax,并且 JS 也比 Lua 更为人熟知,所以也就迅速替代 Wax 成为了热修复的主流选择。
JSPatch 的接入成本非常低,对项目的影响也非常小,不需要引入额外的脚本解释器(因为已经有 JSCore 了),并且 JS 写起来真的比 Lua 要爽很多。
# 异军突起的 React Native 和 Weex
时间继续在走,以 React Native 和 Weex 为代表的各种项目开始冒出来了,他们主要的想法并不是给项目做热修复,而是有更宏大的想法:使用脚本来编写 App 的主要功能。
在 RN 和 Weex 一类的技术里面,JavaScript 是实现原生代码的一种 DSL,通过 Bundle 里面或者下发的 js 文件,原生代码会解析 js 之后做界面渲染的工作。
我个人并没有玩过 RN,但因为在阿里工作过的原因有使用过一段时间 Weex,我个人的感觉是这样的技术驱动力来自于对动态运营的极高要求。
# PY 交易的小程序
技术上并无重大创新但是更为大胆的做法出现了,他直接告诉苹果:我们打算在微信上面加载别的程序,并且起了个名字叫做小程序。小程序推出之后,腾讯和苹果相安无事,我估计这里面是有一些 PY 交易的,事实上从某些角度来说小程序是直接违反 App Store 现存的条款的,但是反正微信就是没事。
# 吊胃口的 DynamicCocoa 和 OCScript
这个其实没有太多好说的,毕竟大家的胃口已经被吊的差不多了,而且按这个样子应该会继续吊下去。
简单说这两个更为激进的方案,希望能够把 JS -> ObjC 这一步都给省了,直接写 ObjC,然后转换到他们自己的一种中间代码,这样的话不需要引入新的学习成本。
PS:作为一行代码都没有就已经 1k 小星星的 repo 来说,DynamicCocoa 这波不亏。
# 动态化的动机在哪里
各种技术的诞生都伴随着各种各样的原因,我们在聊动态化的时候,往往伴随着以下这么几个动机:
动态修复线上 bug
逃避审核,调用私有方法
跨平台开发
开发周期太短,运营需求变化太大
很多历史证明跨平台开发(除了游戏以外)基本是比较扯淡的事情,纯粹这样做的应用大多不会有什么特别的优势,逃避审核和调用私有方法方面也只是少数人才会做的事情。那么修复 bug 和应对需求变化,就成了(国内)很多团队研究这些技术的主流原因。
事实上这件事情在国内外是可以形成鲜明的对比的,这是一个怪现象:
国内应用热衷于做 HotFix
国外应用热衷于做 Code Review
国内的团队总是很急,总是要什么需求就要立刻要,有些甚至是产品经理随意去验证自己的想法,完全不尊重软件研发客观规律的做法。而 App Store 的 Review 是要时间的,所以就开始各种开脑洞想要绕过这个过程。
而很多国外的项目(比如说我现在参与的 Outlook Mobile)完全没有任何动态技术,但是 Code Review 却很严格,开发的周期也比较长,更希望能把问题扼杀在摇篮里。
# 严重之处
其实今天这件事情出来的时候我是表示很震惊的,正如所有 Apple 告诉你的事情一样,这件事情的严重之处在于他的「模糊」,没有人明确的知道 Apple 到底在反对什么,他告诉你很多个可能性,让你自己去找有问题的地方。其实就像中国电影审片一样,没有标准就是最难的标准,甚至有时候会很靠运气。
Apple 在审核条款里面是明确表示不能加载动态的下发脚本,改变应用本身的意图。但是这个定义其实是非常非常含糊的,我认为这不是一个可以明确执行的定义。
什么叫脚本?什么叫程序?
什么样的程度叫做改变了应用本身的意图?
游戏加载脚本是业界通行的做法,Apple 会禁止吗?
为什么 WebView 里面加载 js 就可以呢?
通过 Web 和 Native 来区分是否是动态加载程序这是对的分类吗?
微信小程序到底有什么样的 PY 交易?(逃
总之这件事无论从各种角度来看,都很苹果。
就像飞机上不能携带超过 100ml 的水一样,根本原因是因为安检无法低成本的分辨是否易燃易爆,所以 Apple 非常广泛的发了这样的「震慑」邮件。
# 这样有用吗?
由于 Objective-C 夸张的动态性,其实我们很早就知道,Apple 从技术层面比较难完全防止掉这样事情的发生。
甚至退一步讲,完全不用任何动态技术,也不用任何私有 API,都可以让应用在审核期间看起来和用户用起来是完全不一样的,这种用一个开关来控制的小把戏我想大家都心知肚明。我想 Apple 真正痛恨的应该是这种欺骗,所以他只能尽可能的让这种「变化」还处于他的掌控之中。
毕竟如果一个应用只剩下一个「壳」的话,动态技术可以加载任何内容,App 审核也就形同虚设了,作为一个很有控制欲的公司,这种失控是苹果真正痛恨的。
# 后续发展
从目前来看,Apple 主要针对的是 JSPatch 和 Rollout 一类的热修复框架,这类框架的特点是可以下发后执行任意的 Native Code,而以做 Feature 为主的 React Native 和 Weex 并未被针对。
我个人非常期待这件事的进展,JavaScriptCore 作为一个非常有用的框架,我不希望就这样废掉。甚至我们可以发现,Apple Music 很多地方就是用 JS 实现的,这一点非常有趣。
我希望 Apple 可以在较劲之后找到一个平衡的点,减少自身 SDK 的 bug,加快审核的响应速度,开放一点。
而我们应该做的,就是上线前保证质量,并且祈祷遇到合格的产品经理。(逃
- EOF -
网友评论