本文来自 Tw93 前端专栏, 欢迎交流
序
去年双十一为了排查一些 Weex 性能问题开始接触 iOS 相关学习, 恰逢今年 1 月份开始慢慢参与 iOS 业务开发, 借此机会整理一下 iOS 开发中的一些基础知识, 用于备忘, 同时旨在通过系列文章让前端同学在日后碰到 iOS 的代码可以看懂, 方便更好的理解端上的一些原理
大纲
上主要讲述在 iOS 的开发过程中碰到的文件格式和 Foundation 框架常用的类, 中和下正在整理中
文件格式
.h: 头文件也叫接口文件, 提供对外访问的接口, 包含类方法和属性的声明, 相当于类的用户手册
.m: 实现文件, 用于实现 .h 中声明的方法, 相当于类的工程细节
Podflie: 包管理文件, 用于定义项目所需要使用的第三方库, 类比 package.json
Podflie.lock: 依赖库锁文件, 记录了需要被安装的 pod 的每个已安装的版本, 便于团队协作包统一, 类比 package-lock.json
.xib: 可视化文件, 一个轻量级的用来描述局部界面的文件
.storyboard: 界面布局文件, 承载对应 UIView 的视图控件
.pch: 预编译头文件, 将一个工程中不会常修改代码预先编译好放到 pch 里面, 用于存放公用宏存放公用头文件还可以管理日志输出
.plist: 属性列表文件, 一种用来存储串行化后的对象的文件, 通常用于储存用户设置或者一些捆绑的信息, 也是数据持久化解决方案之一
.lproj: 多语言本地化文件夹, 也即为每一种语言单独定义一份资源, 有图片, 文本, Storyboard,Xib 这些文件
.xcassets: 图像资源文件, 用了存放图片资源
.xcodeproj: Xcode 工程文件, 存储着 Xcode 工程的各项配置参数
.xcworkspace: Xcode 工程文件, 使用 pod install 后生成的新文件, 打开后可以同时看到项目文件和 pods 依赖包
Foundation 框架
简要介绍
首先介绍一下 Cocoa,Cocoa 是一套 OS X 和 iOS (Cocoa Touch) 中的框架和运行时支持, 也即 API 应用程序接口, 可类比于 VC 里面的 MFC
Cocoa 其中最重要和 UIKit(OSX 为 AppKit) 和 Foundation, 两个框架在系统中的位置如下图, 同时可以看到常见的框架所在位置
其中 UIKit 主要用于 iOS 界面构建的, 后面详细介绍
Foundation 框架给我们框架和 App 开发提供了基础层功能, 用于访问基本的数据类型, 集合和操作系统服务, 包括如下:
数据存储和持久化: NSArrayNSDictionary 以及 NSSet 为任何类的 OC 对象提供存储
文本和字符串处理: NSCharacterSet 用于 NSString 和 NSScanner 类中字符串各种操作
文件处理: NSFileManager 类 提供大量用于文件操作检查的方法
日期和日期: NSDateNSTimeZone 和 NSCalendar 类
URL 操作: 提供一组类和协议来处理 URL 访问
异常处理: NSException 类
Foundation 在 JS 里面可以假想成一个原生的公用 Util 库, But 没有...
开始之前
OC 中 @是做什么的 ?
通常字符串前面会加一个 @, 用来将一个 C 语言的字符串转化为 OC 中的字符串对象 NSString
@ 符号 OC 中大部分的关键字都是以 @开头的, 比如 @interface,@implementation,@end @class 等
很多类之前的 NS 前缀是什么 ?
在 OC 里不支持命名空间, 所以只能通过加前缀这种土土的方法来避免命名冲突, 所以常常会用 3 个大写字母(2 个的被苹果占了), 可譬如开发者名称公司名称或者方法名等来区分
NS 代表的是 NextStep, 是 Jobs 在 1985 年离开水果的时候创建的公司名称, 后来被收购回来后, 很多成果被吸收到 OSX 基础中
类似的还有, CF == Core Foundation;CG == CoreGraphics.frameworks;CA == CoreAnimation.frameworks;UI == UIKit 等表示
对象的可变性
OC 中的类可分为可变 (mutable) 类和不可变 (immutable) 类, 这里不可变指的是字符串在内存中占用的存储空间固定, 并且存储的内容不能发生变化, 反之可变是指占用空间可不固定, 内容可修改; 这个和灵活的 JS 是很不一样的, 但我更倾向于对数据类型区分可变和不可变, 也便于后续的维护和扩展
对于确定不会改变的类, 它只具备应有的属性方法, 可变类通过继承原有不变类, 再增加可变方法属性即可实现可变
可变类是不可变类的子类, 同时所有可变类的实例对象可以作为不可变的实例对象来使用
NSString
在 OC 中使用 NSString 来表示字符串, 可变 NSMutableString 继承不可变 NSString 类, 好比一个字符串链表, 可对其进行 增删改 操作
NSArray
在 OC 中使用 NSArray 来表示数组类, 这里需要注意的是它不像 JS 的数组可以存入任何类型, 在 NSArray 中只能够存入 OC 对象, 同时也是不可变的, 假如需要更改数组, 需要使用它的子类 NSMutableArray
通过 arrayWithObjects 方式创建的数组记得在最后一项加上 nil, 所以为了方便更推荐直接使用
@ [@"lnj", @"lmj", @"jjj"]
方式创建, 但是因为生成的是不可变数组, 故在 NSMutableArray 中不要这么创建
很多数组的操作方法和在 JS 中对比大致相同, 不过 NSMutableArray 提供了比 JS 中一些更丰富的方法, 包括交互两个元素位置这种需要自己写的方法, 也即很多时候编写时候可以先查查文档看是否有这个方法, 很多时候可以从里面找到
NSDictionary
前端同学第一次听到字典这个词可能会有些陌生, 可以类比 JS 中的 Object, Java 中的 Map, 字典中的数据也是通过键值对保存
NSDictionary 属于不可变字典类, 一般创建之后只能用于查询, 不能对其进行修改操作, 想修改需要使用 NSMutableDictionary 类
NSDate
NSDate 可以用来表示时间, 用来进行一些常见的日期时间的处理, 它的使用方式和 JS 的 Date 也是很相似的,[NSDate date] 返回的就是当前时间, 但是在格式化日期时间上面 Foundation 处理比 js 要优雅很多, 直接调用其内置方法即可
通过和 NSDateFormatter 类配合使用可以格式化日期, stringFromDate 用于 NSDate 转 NSString,dateFromString 用于 NSString 转 NSData:
- //NSDate 转 NSString
- NSDate * now = [NSDate date];
- NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
- NSString * str = [formatter stringFromDate: now];
- NSLog(@"%@", str);
- // NSString 转 NSDate
- NSString * str = @"2018-02-08 11:53:12";
- NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
- NSDate * date = [formatter dateFromString: str];
- NSLog(@"%@", date);
假如想在时间日期上面处理更复杂的内容, 还可以借助 NSCalendar 类丰富我们的处理:
- NSDate *date = [NSDate date];
- NSCalendar *calendar = [NSCalendar currentCalendar];
- // 利用日历对象获取年月日时分秒
- NSCalendarUnit type = NSCalendarUnitYear
- | NSCalendarUnitMonth
- | NSCalendarUnitDay
- | NSCalendarUnitHour
- | NSCalendarUnitMinute
- | NSCalendarUnitSecond;
- // cmps 包括 yearmonthdayhourminutesecond 属性
- NSDateComponents *cmps = [calendar components:type fromDate:date];
比较日期, 可以直接使用如下方法:
- - (NSDateComponents *)components:(NSCalendarUnit)unitFlags
- fromDate:(NSDate *)startingDate
- toDate:(NSDate *)resultDate
- options:(NSCalendarOptions)opts;
从上面看, OC 中很多方法都是很长很长的, 并且将每一个参数代表的意思也表示出来了, 不会出现错误对应的情况
NSURL
写前端久了会慢慢觉得 URL 就是一条线上的链接 H5 地址, 其实不然, URL(Uniform Resource Locator)中文意思是统一资源定位符, 除了 http 协议之外, 还有 https/ftp/file 协议用来访问一个资源
在 OC 中, 操作 URL 时候应该用专用的 NSURL 类, 而不建议把 URL 当做字符串来手工解析
随着后面的升级, 原来在 OS 中很多文件输入输出的地方也开始使用 NSURL 了, 也即慢慢地将网络当时 OS 中的一部分了
NSNumber \ NSValue \ NSNULL 包裹类
Cocoa Fundation 框架的集合类 (NSArray\NSDictionary\NSset) 中只可以放入对象, 前端同学可能开始时候会很不适应, 假如想放入 int\float\double\bool 等基础类型, 需要将其包裹成 OC 对象在进行放入, 这里就需要使用 NSNumber 包裹类了, 才能间接的将基础类型放入
- // 直接在前面加入 @就可以创建一个 NSNumber 对象型
- @10;
- @10.5;
- @YES;
- @(num);
- // NSNumber 类提取基础型
- - (char)charValue;
- - (int)intValue;
- - (double)doubleValue;
- - (BOOL)boolValue;
但是对于结构体指针这种复杂的数据类型, NSNumber 也无法包裹, 这里需要使用 NSValue 类, 它是 NSNumber 的父类, 可以将任意类型包装成对象
- // 结构体包裹, 譬如 NSPointNSSizeNSRect
- - (NSValue * ) valueWithPoint: (NSPoint) point;
- // 从 NSValue 对象取出之前包装的结构体
- - (NSPoint) pointValue;
前面的数组字典中是不能自己放入 nil 的, 因为在里面有结束的含义, 但是有时候我们又确实需要一个特殊的对象来表示空值, 这里就需要 NSNULL 了, 可以通过类方法 +(NSNull *)null 来定义, 同时可以通过
if (obj == [NSNull null])
来判断是否为 NSNull
小结
通过以上文章, 最多只能够让大家对 OC 编程有一些了解, 谈不上深入, 更多的是希望前端对于 H5/Weex 的深入了解和优化不要局限到最上面薄薄的一层, 完全可以联系到底层的加载渲染图片库网络库等等来进行优化
从语言层面来说, OC 和 JS 还是挺有意思的, 变量 / 方法命名的风格正好相反, 苹果更加鼓励开发者程序命名尽量使用英文全称并且是尽可能详细, 通过看到方法和变量就知道是做啥的, 看写得好的 iOS 仿佛看散文一样, 譬如 "-(NSArray *) componentsSeparatedByString:(NSString )separator;" 分割字符串, 在 JS 里面直接通过 "str.split()" 就解决了, 这样和 JS 的加载有关系, 因为需要从网络下载, 尽管也有压缩工具, 但大家也习惯了短命名来表示
由于编译型语言缘故, 写 OC 过程中很多错误可以在编译过程就发现, 同时运行速度快, 同时苹果很多事情都帮开发者做了, 这样其实写 iOS 可发挥的地方没有前端多; 反之 JS 为解释性, 更多依赖于引擎, 语言很动态, 写起来超爽, 同时很多奇淫技巧, 运行地方不太受限制, 这样可以产出比较多好玩的东西
各有优点, 喜欢 web 的快速和开放, 也爱 iOS 的优雅体验和统一化
知乎阅读体验不是很好, 假如你可翻墙, 可见 GitHub Pages 写给前端看的 iOS 梳理 (上)
来源: https://juejin.im/entry/5a7c1e39f265da4e8c44f80a