月度归档:2015年04月

iOS依赖注入

依赖注入(dependency injection)参考资料,objc.io上一个关于DI的详细介绍依赖注入
关于iOS runtime swizzle的介绍,传说中的移形换位大法swizzle
GitHub上一个开源的iOS DI实现:objection
GitHub上一个开源的swizzle封装:jrswizzle

依赖注入的几种形式:

  1. 构造器注入
  2. 属性注入
  3. 方法注入
  4. 环境上下文
  5. 抽取和重写调用

一句话介绍:给对象传入实例变量,用实例变量储存依赖对象,并用这个变量来进行各种操作。

swizzle简介

Method swizzling指的是改变一个已存在的选择器对应的实现的过程,它依赖于Objectvie-C中方法的调用能够在运行时进改变——通过改变类的调度表(dispatch table)中选择器到最终函数间的映射关系。	    

一句话介绍:selector和imp是一一对应,但是使用这技术可以在运行时修改selector对应的imp,imp类似指针,指向方法具体实现,swizle的作用就是修改指针具体指向。

iOS堆栈

刚写完一个功能模块,打算写点基础知识:就简单介绍下堆栈和一些属性相关的基础知识。

堆栈是两种数据结构:
栈stack是系统自动分配自动回收空间。
堆heap是手动申请空间手动释放。

就速度而言,stack的存取肯定快于heap,这是基本知识。对象都是在heap上手动alloc,而struct(CGRect,CGPoint等),nonobject type(NSInteger,CGFloat,BOOL等)这些都是在stack上分配。
创建obejct比创建struct多一些额外开销,这可以给自己一点启示,就是说有些时候定义方法或者说写功能库的时候,有些参数可以定义为struct,当然里面的数据必须都是nonobject。

既然说到效率,想起我这机器有点老,编译稍微大点的项目会很慢,所以为了提高编译速度,在一个头文件中尽量少引入其他头文件。
尽量使用forward declaring,@class这种方式,因为很多时候在头文件种
#import class
只是为了知道有个这个类名而已。直接使用
@class MyClass
这种向前声明即可,这样我们就能在头文件方法定义中使用
- (void)test:(MyClass *)class;

引入头文件的时机尽量延后,减少编译时间。
还有一点少用#define多用类型常量。
#define kTableGroup = @"groups"
替换为
.h
extern NSString * const kTableGroup;
.m
NSString *const kTableGroup = @"groups";

关于React Native的猜想

刚出React Native的时候看了资料学习了些,给公司那边简单培训了一下,整个开发更接近前端,react,jsx,virtual dom,flexbox全是些很新的东西,就核心理念还是通过javascriptcore来实现一套从js到oc的通信。

这条路一直是能走通,只是之前像phonegap这类为了实现一套代码全平台通用,基于webview,实现的是Hybrid App。

其实自己搜索了下,发现用js实现native功能的框架还有NativeScript,原理并不难,只是说抛弃了webview,自己来实现各种标签对原生组件的调用。NativeScript基于类似nodejs的包管理系统,用module对不同平台的原生代码进行统一封装。
随便打个比方,打印语句,在iOS上是NSLog,Android上是System.out.println,他统一后在js端可能就一句print,对应到不同平台解析成不同的源代码。

着重说React Native,他的推出之所以这么火,一个是因为是facebook出品,另一个就是说他基于react这套框架,本身有一定的用户基础,对于react我也不甚了解,不过看文档,一个jsx代码更直观,一个virtual dom让渲染更加快捷,能提升性能。
在移动平台性能很重要的情况下,如果速度更快,同样功能下,那就更可能让多的人投入进来。

React Native这套框架实现了很多本地原生组件的封装,包括一些常用功能性API的封装,代码的核心是RCTBridgeModule,具体可以看文档,不过实现一个网站的客户端是没问题的,包括注册登陆,增删改查,基于nav+tab。

另外还有一个好处,对于使用IDE的人来说,调试界面恐怕最痛苦的,修改代码,编译到模拟器。react native直接修改js,在模拟器端command+r即可看到效果。

但是,如果对一个App ui动画上要求很高,恐怕用这类成本上有点高。既然都是组件,理论上自己也能继承RCTBridgeModule来实现自己特性的组件。那就得对oc有一定的了解,未来如果出了react native android的版本,那就还得了解java才行。

不过react native的出现,让不少大公司也开始研究使用这套技术,这个也是趋势,learn once,write anywhere。

但是由于安卓端可能在今年10月份才出来,估计到明年才会真正的流行起来,现在所做的就是技术储备,对于oc开发而言,出了学习jsx,react外,还得了解下他的理念,知道如何写插件,定义自己的组件。
然后不远的将来,oc,java写iOS或者Android只是为react写组件。界面布局,通信什么的还是在js上实现。

NS_OPTIONS & NS_ENUM

这两个是iOS6后才有的定义enum的宏定义。

之前没细琢磨NS_OPTIONS,看到定义中有移位运算,看得懂代码,但是并不知道作用。

enum 也可以被定义为按位掩码(bitmask)。用简单的OR (|)和AND (&)数学运算即可实现对一个整型值的编码。每一个值不是自动被赋予从0开始依次累加1的值,而是手动被赋予一个带有一个bit偏移量的值:类似1 << 0、 1 << 1、 1 << 2等。如果你能够心算出每个数字的二进制表示法,例如:10110 代表 22,每一位都可以被认为是一个单独的布尔值。例如在UIKit中, UIViewAutoresizing 就是一个可以表示任何flexible top、bottom、 left 或 right margins、width、height组合的位掩码。

不像 NS_ENUM ,位掩码用 NS_OPTIONS 宏。

语法和 NS_ENUM 完全相同,但这个宏提示编译器值是如何通过位掩码 | 组合在一起的。同样的,注意值的区间不要超过所使用类型的最大容纳范围。

看到很多开源代码中使用<<这种移位定义枚举,实际上就是说明一个值能够对应多个枚举,想autoresizingMask这个属性一样,实际上可以对应的也不止两个。

另外就是switch中处理enum类型不要用default分支。这样如果你枚举没处理完编译器会给出警告,能让我们及早的发现问题。包括你如果后来添加了枚举,没添加处理,也会警告,避免遗忘。