iOS / Objective-C 在 API 层面与 Android / Java 的类比

其实在 11 年学 Android 开发之前,本来是准备学 iOS 的。后来因为某些原因([之前文章](/blog/2013/06/25/dev/)有提到过),不得不作罢。

去年入手了 Macbook Pro ,但那时候还纠结于”App or Game”的路线问题,今年初才正式将学习 iOS 提上日程。

之前在公司闲暇时看过 Objective-C Succinctly
了解了一些基本的 Objective-C 语法。

最近通过《iOS 7: iPhone / iPad 应用开发技术详解》进行了系统的学习,
但不得不吐槽,这货绝对是我读过的最烂的一本技术书.

  • 语法篇完全不按常理出牌,一上来就大讲特讲 Objective-C 2.0 的”新特性”(其实 06 年 WWDC 就发布了);然后把面向对象都讲完了,才通过 Foundation 提到数据类型等对语言来说最基本的东西;不禁想请教作者:您当年的英语老师是讲完语法才教 26 个字母的么?

  • 类/对象/消息三章完全没有条理,很多重复内容,在 89 页甚至有整段重复的情况;

  • 很多章节连基本概念都没介绍清楚,就一味的去列举各种 API ,而且 Demo 也不够详细;有网友吐槽不少内容都是直接照搬的以前的教程和文档,包括插图和 Demo ;

  • 很多对日常开发来说非常重要的技术都没涉及:网络相关(HTTP / JSON / XML)、多线程、IO 及资源访问、SQLite 、Animation 等;

  • 标榜 iOS 7,但通篇基本看不到任何 iOS 7 新特性,比如 NSURLSession 等。

总体来说,这本书感觉不伦不类,想不到居然能被 CocoaChina 推荐(链接),当然我并不是因为这个才去买书的。

言归正传,由于具备 Android 开发经验,很多东西都是相通的,自然比当年学 Android 轻松多了:

平均每个晚上过完 1 ~ 2 章,包括写 Demo

对于书中未讲透彻或者纯粹未涉及的内容,当然就只能自行查阅资料了。

每当接触一个新概念,都会习惯性地去思考:这个在 Android 或 Java 中能找到类似的么?

所以这篇学习笔记侧重于 iOS / Objective-C 和 Android / Java 的类比,由于对底层尚不甚了解,主要是 API 层面的。

语法部分

基本属性

Objective-C 是 C 的严格母集,C / C++ 代码不用修改就可直接通过 Objective-C 编译器;因此你在代码里既可以使用宏定义、指针和各种预编译指令,也可以使用内联函数和结构体。虽然它在 C 基础上加上了面向对象特性,本质上还是编译型语言;

而 Java 摒弃了 C++ 的指针而改用引用,是一门完全面向对象的语言,必须通过 JNI 间接调用 C / C++ ,而且运行在 JVM 上,典型的解释型语言。

数据类型

和大多数面向对象的语言一样,Objective-C 对基本数据类型进行了封装,并提供了集合类:

  • NSArray 类似于 Java 中的 ArrayList / Vector ;

  • NSSet 类似于 Java 中的 Set ;

  • NSDictionary 类似于 Java 中的 HashMap ;

  • NSEnumerator 类似于 Java 中的 Iterator ;

当然也有一些特殊的类型:

  • id 类型是一个指向对象实例的指针;

  • SEL 类型是 selector 类型,即指向方法的指针;

  • nilNULL 相同,但 Nil 是一个指向空的类;

方法定义

Objective-C 定义方法时强调可描述性:必须在每个参数之前指定其描述,而且这些描述也是函数本身 selector 的一部分;可能一开始会觉得这样比较冗长,但这样确实一眼就能看出各个参数的含义,而不用去看文档。
特别是在参数类型和个数相同的时候,能避免仅仅通过函数名判断而执行错误的调用。

消息与动态绑定

Objective-C 的最大特色便是继承自 SmallTalk 的消息传递模型,在 C / C++ / Java / C# 等主流语言中早已习惯得通过 . 或者 -> 执行的函数或方法调用,到了 Objective-C 里面成了通过 [] 发消息。

当然这只是形式上的,本质上不同的是其动态特性:编译的时候并不知道执行哪块代码,运行的时候通过 selector (SEL 类型表示方法名/签名的指针)动态查询函数入口地址。

相关的方法有:

  • isKindOfClass: / isMemberOfClass: 检查对象是否在指定的类继承体系中;

  • respondsToSelector: 检查对象能否响应指定的消息;

  • conformsToProtocol: 检查对象是否实现了指定协议类的方法;

  • methodForSelector: 返回指定方法实现的地址;

  • performSelector: / performSelector:withObject: 执行 SEL 所指代的方法;

这里想到了 Android 开发中的一个场景:

如果使用新的 API 方法,用高版本 SDK 编译,但很容易在旧的设备上报错;或者使用过时的 API 方法,而在新的设备上运行也有类似问题。通常的做法无非这么几种:

  • 根据系统版本执行不同方法;

  • 通过 @deprecated@targetAPI 等注解;

  • 通过 try / catch 捕获异常;

而且你往往得通过查看官方文档,来了解这个方法什么时候加入的或者什么时候过时的。

而在 Objective-C 中直接通过 respondsToSelector: 判断是否响应该方法即可轻松解决。

内存管理

不同于 Java 与生俱来的 GC ,Objective-C 2.0 以前并不支持垃圾回收。

NSObject 类提供的手动管理-引用计数机制:

  • alloc 方法创建并持有对象,引用数 +1 ;

  • init 方法初始化对象,引用数不变;

  • new 方法创建并初始化对象(类似 C++),引用数 +1;

  • retain 方法持有对象,引用数 +1;

  • release 方法释放对象,引用数 -1;

  • autorelease 方法超出作用范围自动释放对象,引用数 -1;

  • dealloc 方法通知系统释放引用数为 0 的对象;

  • copy/mutableCopy 方法根据原有对象深度拷贝,创建并持有新对象;原对象引用数不变,新对象引用数 +1;

  • retainCount 方法返回当前对象引用数;

  • 引用计数相关方法内部都会调用 _CFDoExternRefOperation 方法,通过操作一张 Hash 表管理引用数;

声明属性时:

  • assign: 直接赋值,不影响引用计数;常用于基础数据类型;

  • retain: 引用数 +1,释放旧对象,赋值新对象;常用于 NSObject 及其子类对象;

  • copy:直接深度拷贝创建新对象;常用于 NSString

AutoReleasePool:

  • NSAutoReleasePool 中的对象可以存活到对象池本身被销毁(dealloc)的时候,然后它会向所有对象发送 release 消息;但是由于它延缓了对象释放,容易内存泄露,不适合作用于大量对象;

  • 通过定义方法返回的对象强引用会被自动注册到 AutoReleasePool,这和其他语言(C++/Java)局部变量位于 Stack,超出作用域自动回收是一致的;

  • alloc/new/copy/mutableCopy 方法创建的对象是自己创建自己持有,不会加入 AutoReleasePool;

  • ARC 提供了替代者 @autoreleasepool 代码块;

ARC(Automatic Reference Counting):

  • 在编译后自动加上 retainrelease ,所以代码中不能手动调用;且只能作用于 Objective-C 代码;

  • 如果想在同一项目中让 ARC 和 非 ARC 代码共存,可以在 Build Phases -> Compile Sources 中对单个文件通过 -fno-objc-arc 关闭 ARC,或者 -f-objc-arc 打开 ARC;

同 Java 类似, Objc 也有引用类型:

  • __strong: 默认的强引用类型,非 ARC 情况下超出作用于不会自动回收;

  • __autoreleasing:不会增加引用数,对象被加入 AutoReleasePool;能解决循环引用,常用于修饰 delegate;

  • __unsafe_unretained:类似 __autoreleasing,但置为 nil 后会立即释放内存,可能引发野指针 crash,所以 unsafe

  • __weak:类似 __autoreleasing,新加入,只能在 ARC 模式使用;

  • 一般遵循:父对象持有子对象强引用,子对象持有父对象弱引用;

KVO

KeyValueObserver 机制通过调用 addObserver:forKeyPath:options:context: 方法可监听对象属性值的改变,有点类似于 Android 中的 ContentObserver

异常捕获

除了需要在 try / catch / finally 关键字前面加 @ 以外,用法同 Java 基本一致。

闭包

Objective-C 2.0 加入了 Block 支持,用的最多的恐怕就是将空的 Block 当参数作为 Callback ,这其实就是闭包的思想,嵌套 Block 就类似于高阶函数。

Java 中虽然可以通过 Interface 和匿名内部类实现类似功能,但太过繁琐。真正的 Lambda 表达式直到刚发布的 Java 8 才加入,而 C# 则在好几年前发布的 3.0 版本就提供支持了。

面向对象

  • 和 Java 类似,NSObject 是根类,而且只允许单继承,但可是现多个协议;

  • Objective-C 中的 superself 关键字类似于 Java 中的 superthis 关键字;

  • Objective-C 新建对象不同于 C++ / Java 中直接采用 new 关键字,而是一般分为 alloc (分配内存)和 init (初始化)两个步骤,当然如果你不需要保持对该对象的所有权可直接调用工厂方法;

  • 由于继承了 C / C++ 中的定义与实现分离的思想,Objective-C 中的 interfaceimplementation 并不具备 Java 中的 interfaceimplements 的面向对象含义:只不过一个用于头文件中作定义,一个用于实现而已;

  • Objective-C 中真正具有接口功能的是 protocol ,不过与 Java 不同的是 protocol 可以指定哪些方法是可选的哪些方法是必须实现的,这样就不需要类似 Java 中的抽象类了;

  • Category 也是 Objective-C 的一大特色,可以不用继承就直接添加新方法,但不能添加属性;

  • Extension 可以看作是内部匿名 Category ;

UI 部分

UIApplicationDelegate

UIApplicationDelegate 从字面意思看很像是 Android 中的 Application ,但其实从它的各种回调方法看,它还具备 Android 中 Activity 管理应用生命周期的功能:

  • applicationDidFinishLaunching:application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions: 类似于 ActivityonCreate() 方法;

  • applicationDidBecomeActive: 类似于 ActivityonStart() 方法;

  • applicationWillEnterForeground: 类似于 ActivityonResume() 方法;

  • applicationWillResignActive: 类似于 ActivityonPause() 方法;

  • applicationDidEnterBackground: 类似于 ActivityonStop()onSaveInstance() 方法;

  • applicationWillTerminate: 类似于 ActivityonDestroy() 方法;

除了生命周期,UIApplicationDelegate 还管理通知和状态栏变化等。

这样看来,在应用和页面管理上,iOS 和 Android 有很大区别:

  • iOS通过 UIApplicationDelegate 几乎包办了所有的事情(管理应用生命周期并控制 Window 的内容),无愧”应用代理”的名称;

  • Android 将应用管理和页面管理分开,而且每个页面( Activity )有各自独立的生命周期并以 stack 形式存储;大多数情况下通过 Context 对象进行应用全局的操作:如启动服务、接收广播、发送通知等;

UIViewController

UIViewController 非常类似于 Android 3.0 中加入的 Fragment ,可以多重嵌套,也可以添加 View 。生命周期相关回调方法也很类似:

  • loadViewviewDidLoad 类似于 FragmentonCreateView() 方法;

  • viewWillAppear: 类似于 FragmentonStart() 方法;

  • viewDidAppear: 类似于 FragmentonResume() 方法;

  • viewWillDisappear: 类似于 Fragment onPause() 方法;

  • viewDidDisappear: 类似于 FragmentonStop() 方法;

  • viewWillUnloadviewDidUnload 类似于 FragmentonDestroyView() 方法;

UIWindow 和 UIView

  • 和 Android 一样,iOS 也只允许同时显示一个 Window,其中可以添加 View(一般通过 UIViewController 间接添加);

  • Nib / Storyboard 类似于 Android 中的 layout.xml 布局文件,当然 iOS 也支持像 Android 一样直接用代码生成 View;

  • UIView 两个构造方法中,initWithFrame: 类似于 Android 中的默认构造方法,而 initWithCoder: 则类似于 Android 中带 AttributeSet 参数的构造方法;

  • 如果对画面实时性要求很高,需要使用 OpenGL 实时绘制,可以使用 GLKViewGLKViewController ,类似于 Android 中的 SurfaceViewSurfaceHolder ;

  • 两个设置布局方法 setNeedsLayoutlayoutIfNeeded 类似于 Android 中的 requestLayout()forceLayout() 方法;

  • 三个显示方法 drawRect:setDeedsDisplaysetNeedsDisplayInRect: 类似于 Android 中的 onDraw()invalidate() 方法;

  • UIView 添加 View 的相关 API 比 Android 要强大,bringSubViewToFront:sendSubViewToBack:exchangeSubviewAtIndex:withSubviewAtIndex: 这些比 Android 中的 add 和 remove 要方便的多;

  • UIView 提供 subView 的 add / remove 回调方法,而 Android 中 WindowManagerView 均没有;

至于各种控件,说实话感觉各种系统基本都差不多,不过 iOS 有几个比较特殊的复合组件:

  • UINavigationController 用于顶部页面层级导航;因为不像 Android 中的 Activity 默认以 Stack 存储,要使用这个东西自己实现;

  • UITabBarController 用于底部并列式标签页切换;相当于之前 Android 中的 TabHost ,不过现在官方的 Android Design 推荐 ViewPager

  • UISplitViewController 用于实现页面分割,比较常见的就是设置页面,不过只有横屏才能看到效果;

CALayer 和 Animation

  • iOS 中有层的概念,所有 View 都是在 CALayer 上绘制的,而 Android 只能通过 FrameLayoutRelativeLayout 实现类似”层”的效果;

  • 调用 UIViewsetAnimationDelegate:setAnimationWillStartSelector:setAnimationDidStopSelector: 三个方法可在动画开始前和停止后执行特定操作,类似于 Android 中的 AnimationListener ;

  • 通过设置 CALayer 属性可实现特殊效果,也可以添加动画,支持路径、rotate 、alpha 、scale 等变换;这些效果 Android 除了路径其他都有,而且 Android 3.0 提供了强大的 PropertyAnimation ,只要提供 getter 和 setter 方法,就能自动在 duration 时间内按照某种规律改变属性的值。

  • CAAnimationGroup 可执行一系列动画,类似 Android 中的 AnimationSet ;

事件处理

由于 UIViewUIViewController 都是直接或间接继承自 UIResponder ,可直接通过重写相关方法处理触摸事件:

  • touchesBegan:withEvent: 类似于 Android 中的 ACTION_DOWN 事件;

  • touchesMoved:withEvent: 类似于 Android 中的 ACTION_MOVE 事件;

  • touchesEnded:withEvent: 类似于 Android 中的 ACTION_UP 事件;

  • touchesCanceled:withEvent: 类似于 Android 中的 ACTION_CANCEL 事件;

iOS 主要通过调用 UIApplicationbeginIgnoringInteractionEventsendIngoringInteractionEvents 方法,以及重写 UIViewhitTest:withEvent: 方法来对触摸事件进行拦截和分发;而 Android 中通过 dispatchTouchEvent()onInterceptTouchEvent() 方法实现;

iOS 由于没有返回键,键盘关闭需要通过调用 UITextFieldresignFirstResponder 去通知系统,并根据通知返回的键盘高度图动态调整布局;而 Android 只需要通过配置文件以及布局上的小技巧即可实现布局自动调整。

多线程

NSOperation

NSOperation 类似 Java 中的 Runnable ,不用关心线程管理和数据同步,要么使用其子类 NSInvocationOperationNSBlockOperation ,要么继承并重写 main 方法;

NSOperationQueue

NSOperationQueue 类似 Java 5 中加入的并发 API :BlockingQueueExecutor ,用于实现线程池;同样可以设置容量和最大并发数;

NSThread

NSThread 类似 Java 中的 Thread ,需要自己管理线程生命周期和同步;可以调用类方法 detachNewThreadSelector: 和实例方法 initWithTarget: 创建线程;

NSLock 与 NSCondition

NSLockNSCondition 用于线程安全、同步与通信,前者提供简单的锁功能,后者可发送 waitsignal 消息实现等待和唤醒,类似 Java 中的 wait()notify() ;

GCD

GrandCentralDispatch 用于并发和多核编程,底层依然是采用 NSThread 实现,且基于 FIFO ,但使用更便捷,直接调用函数即可:

  • dispatch_apply 可让一个任务执行指定的次数;

  • dispatch_get_main_queue 用于获取主线程任务队列;

  • dispatch_syncdispatch_async 分别用于任务的同步和异步执行;

  • dispatch_group_syncdispatch_group_async 用于实现多个任务按序执行,通过调用 sleepForTimeInternal 控制执行顺序;

  • dispatch_barrier_async 用于在任务序列中设置”屏障”,阻塞后面任务的执行(即使 sleepForTimeInternal 设置很小也没用);

后台任务与 UI 更新

和 Android 一样,iOS 也只能在主线程更新 UI ;因此在启动新线程执行完数据操作后,必须指定在主线程执行 UI 更新。

可以通过 NSOperationNSThread 、GCD 执行后台任务,还可以直接调用 NSObjectperformSelectorInBackground:performSelectorOnThread: 方法;

然后调用 dispatch_async(dispatch_get_main_queue, ^{ }) 或者 performSelectorOnMainThread: 方法更新 UI ;这两个方法类似 Android中 AcitivityrunOnUIThread() 方法,当然通过 Handler 或者 AsyncTask 也可实现;

但在 Android 中还有个问题就是,Activity 和后台任务因为引用关系,可能造成 Activity 无法及时回收资源或者当后台任务执行完要更新 UI 发现界面已被销毁;也就是说后台任务和 UI 页面没有一个统一的生命周期管理机制;

Android 3.0 加入的 Loader / AsyncTaskLoader / CursorLoader 解决了这一问题,它将 AsyncTaskContentObserverActivity 整合起来,在 Activity 内部统一进行调度;iOS 中 UIApplicationDelegate 的两个回调方法提供了类似的功能:

  • application:performFetchWithCompletionHandler:

  • application:handleEventsForBackgroundURLSession:completionHandler:

数据相关

SandBox 机制与数据安全

之前听过这么一句话:”越狱的 iOS 设备都比 Android 设备安全,就算不 ROOT 也差不多。” 当时觉得可能有些夸大其词,现在觉得还是有些道理的。

iOS 和 WindowsPhone 一样,文件系统都是不透明的,作为普通用户你不知道我这个文档到底位于哪个目录(绝对路径),当然作为两个 OS 的设计者,他们应该都认为用户根本没必要知道这个。

iOS 每个应用的数据都被限制在单独的 SandBox 内,彼此不能访问,主要包含这些目录:

  • Documents 存储文件数据;

  • Library/Caches 缓存数据;

  • Library/Preferences 用户偏好数据;

  • temp 临时文件;

其中 DocumentsLibrary 两个目录会被 iTunes 同步。

Android 中也有类似的 SandBox 机制,主要目录如下:

  • cache 缓存;

  • databases SQLite 文件;

  • files 普通文件;

  • lib C / C++ 编写的 .so 静态库文件;

  • shared_prefs 用户偏好数据;

但 Android 这个 SandBox 更像是个形式上的,虽然也是按照每个应用对数据做了隔离,但并不能很好的保证用户的数据安全。
首先 Android 中每个应用的 SandBox 都是严格按照包名划分目录的,而在 iOS 中你会发现代码中打印出来的绝对路径是一串无意义的字符。

不要说普通用户一般不知道应用的包名,想想 Android 中那些所谓的垃圾和缓存清理应用吧,他为什么能帮你清理每个应用的缓存?
就是因为他摸请了市面上主流 APP 的包名(ROOT 后文件系统一览无遗),而且因为 Android 系统靠包名识别一个应用,应用开发商考虑到升级问题肯定不会没事去改变包名。

这是 Android 的 SandBox 路径导致的安全问题,再来看看 Android 在 API 层面的数据安全漏洞:

  • 在 Android 中操作 SharedPreferences 和应用目录下的文件时,需要指定一个 Mode 参数,而在 4.2.2 以前居然有 MODE_WORLD_READABLEMODE_WORLD_WRITABLE 这两个危险的常量,可以跨应用读写数据,这个跟 SandBox 的应用数据保护机制完全是相悖的;

  • 同样是 4.2.2 才修复的另一个漏洞是关于 ADB 的,之前是可以通过 adb connect 命令静默的远程连接并监听局域网内的 Android 设备的,修复之后会弹窗提示,而且区分设备指纹信息;

  • 还有一个是关于短信收发的。首先你只需要声明相关权限即可通过 SmsManager 发送短信,也可通过 BroadcastReceiver 监听来信;应用安装时的权限提示大多数用户是不会看的,而且调用 API 时也并不是所有设备都会提示。

在 Android 4.4 之前你甚至可以通过 ContentObserver 直接监听系统短信数据库更新,新的 API 做了限制,只有系统默认短信 APP 才有这个权限;

这还只是没有 ROOT 的 Android 设备,而 iOS 设备的越狱并不等同于 ROOT ,应用仍然被严格限制在 SanBox 之内,而且只要你依然通过 AppStore 安装和更新 APP ,基本上还是安全的。

更多关于 iOS 越狱后的安全问题可参考知乎上的讨论

NSDefaults / SettingsBundle

  • NSDefaults 提供用户偏好数据的存储,类似 Android 中的 SharedPreferences ,也是以 XML 形式保存;

  • SettingsBundle 提供用户偏好数据的展示,类似于 Android 中的 PreferenceActivity ;不过它要更强大:不仅可以不用写界面,而且被集成到了系统设置菜单;

资源访问

  • NSSearchPathForDirectoriesInDomains 用于搜索 SandBox 中的资源路径;

  • NSBundle 提供了对应用中本地各种资源(图像、音视频、plist 、Nib 等)的访问,相当于 Android 中的 AssetManager

文件操作

首先 iOS 内置的很多数据类型都直接提供了持久化存储的 API :

  • NSString / NSMutableString ;

  • NSArray / NSMutableArray ;

  • NSDictionary / NSMutableDictionary ;

  • NSData / NSMutableData ;

通过 writeToFile:atomically: / writeToFile:options:error: 方法可以直接写入 XML 文件;

同时也可通过 initWithContentsOfFile: / initWithContentsOfFile:options:error: 从文件读取数据;

虽然这个只能用于轻量级的文件和数据存储,但不得不说确实很方便;

专业级的文件操作需要通过 NSFileManager 实现,可以实现目录的创建和删除、文件数据的写入和读取等。

CoreData

很多人觉得 CoreData 很像一个轻量级的 Access ,但我觉得它更像一个轻量级的 ORM 框架:

对数据库操作做了封装,基本不用写 SQL ,关键是和 JavaEE 中著名的 ORM 框架 Hibernate 一样繁琐,当然也同样遭到了不少反 ORM 人士的反感。

CoreData 的一些主要 API :

  • NSManagedObjectModel 相当于数据库对象,通过指定 Model 文件名和路径得到实例;

  • NSPersistentStoreCoordinator 负责底层数据操作,通过 NSManagedObjectModel 得到实例;一般不会直接去操作它,而是通过 NSManagedObjectContext

  • NSManagedObjectContext 用于执行数据操作并跟踪记录变化,实例化后需要设置 NSPersistentStoreCoordinator 实例;

  • NSFetchRequest 数据操作对象,可通过指定 limit 和 offset 实现分页;

  • NSEntityDescription 相当于数据表;

  • NSPredicate 用于指定查询条件;

  • NSSortDescriptor 用于指定结果排序方式;

  • NSManagedObject 相当于一条数据记录;

  • NSFetchedResultsController / NSFetchedResultsControllerDelegate 用于监听 CoreData 数据变动以更新 UI ;

Apple 官方的说法是 CoreData 做了很多性能优化,方便重构。

虽然它内部可使用 XML 、Memory 、Atomic 、SQLite 几种存储方式,但你无法通过 CoreData 显示地创建数据库,因为这些 API 是私有的,你只能导入一个存在的 SQLite 文件。

SQLite

可能是 Apple 更推荐开发者使用 CoreData 的缘故,iOS 中提供的 SQLite 相关 API 并不像 Android 中的 SQLiteOpenHelper 那样友好,
没有对常用的增删改查等操作做封装,只能通过执行原生的SQL实现,基本上是原始的 JDBC 式的操作方式:

  • sqlite3_open() / sqlite3_close() 打开/关闭数据库;

  • sqlite3_exec() 执行 SQL 语句;

  • sqlite3_prepare_v2() 执行查询并返回一个 sqlite3_stmt 对象,这东西乍一看还以为是 JDBC 中的Statement ,但其实它已经是执行 SQL 后的ResultSet 了;

  • sqlite3_step() 遍历结果集;

除了 iOS 原生的 API ,还可以使用 FMDB ,一个使用较广泛的第三方 SQLite 封装库;

网络通信

iOS 中网络请求主要使用 NSURLConnection 创建链接,然后创建 NSURLRequest 或者 NSURLMutableRequest ,前者比较轻量级,而后者能设置 method 、添加 header 等,支持发送同步或异步请求;

类似于 Java 中的 URLConnection ,不过显然 NSRURLConnection 更加强大,在 Android 中往往要借助第三方 Apache 的 HttpClient ,以满足各种 method 、header 、持久链接、自动重定向等功能;

iOS 7 添加了新的网络请求 API :NSURLSession ,其中还包括 NSURLSessionConfigurationNSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTask ;提供了配置每个会话的缓存、协议、cookie 和证书政策,甚至跨应用程序共享它们的能力。

类似地,Google IO 2013 也发布了新的网络 API :Volley ,提供了强大的网络管理、请求管理( RequestQueue )、磁盘和内存管理等模块。

除了使用 iOS 原生的网络 API ,还可使用 AFNetworking ,一个使用较广泛的第三方网络封装库。

XML 与 JSON 解析

  • iOS 默认采用 SAX 方式解析 XML ,适合读取大型文档,但不能修改数据;当然也可以使用第三方的类似 Java 中的 DOM4J 的类库,比如 TouchXML ;

  • iOS 中的 NSSerialization 可直接将 NSURL 得到的 NSData 数据序列化为 NSArray 数据;而 Android 中除了自带的 JSON 解析 API ,还可以使用 Google 的 GSON 库,通过为 Bean 类的属性添加注解,即可实现 JSONString 、JSONObject 、BeanObject 三者自由转换;

其他功能

通知

  • NSNotification 是应用内的通知,但不会显示在通知中心,类似 Android 中的 Broadcast ;

  • UILocalNotification 相当于 Android 中的 Notification ,会显示在通知中心;每个应用本地通知不得超过 64 个,否则被丢弃;

  • RemoteNotification 属于推送通知,统一由 APNS 做中介,并在 iOS 后台保持长连接;而 Android 除了 GCM ,各种厂商也提供了自己的推送服务,第三方的服务也不少,如 JPush 、个推等;

  • 通知的提示音不得超过 30 秒,否则会被系统原生替换;通知的内容不得超过 256 字节;

  • 远程通知提供者如果有异常行为,会被 APNS 吊销证书,甚至中断服务;

  • NSNitificationCenter 来自 OS X 10.0 ,而 NotificationCenter 是 iOS 5 引入的概念,二者没有任何关系,就像 JavaScript 和 Java 一样;

总体来说,iOS 具备一个统一而严格的通知系统,虽然各种限制对开发者来说可能不够自由,但确实最大程度地保证了用户体验和服务的可靠性;

而反观 Android,很可能用户一个设备后台同时跑了好几个推送服务:ROM 自带的、应用开发商集成的各种第三方 SDK ,它们各自为政,而且没有 APNS 一样严格的管控。这样不仅白白增加了系统开销,更极大地损害了用户体验(经常会突然收到某个应用 10 多条推送消息,然后直接把通知中心给搞死了)。

多媒体

  • CoreAudio 提供底层支持;硬件解码性能好,但不能多个同时播放;

  • AVAudioRecorder 类似 Android 中的 MediaRecorder ,提供音频录制;

  • AVAudioPlayer 类似 Android 中的 MediaPlayer ,提供音频播放;

  • MPMoviePlayerController 类似 Android 中的 VideoView + MediaController ,用于视频播放;

  • AudioQueueService 支持回放及多路播放;

  • iOS 中的 OpenAL 框架支持混音,使用 CoreAudio 的 IO 单元进行回放,效率较高;Android 中类似的为 2.3 后 NDK 加入的 OpenSL ES;


更多关于 Objective-C / iOS / OS X 的内容,目前正通过《Effective Objective-C 2.0》《Objective-C 高级编程: iOS 与 OS X 多线程和内存管理》进一步深入学习。

然后关于 OpenGL 这块内容,个人觉得除了图形图像等特殊领域,或者写游戏引擎,一般绝大多数应用应该都用不到;至少我做了两三年 Android 开发,除了当初学的时候写过几个 Demo ,实际项目都没怎么用到过。

后来写 Cocos2d-X ,也接触的不多,因为常用操作引擎都做了封装的。

所以这部分稍微了解下即可,深入学习可参考: Learning OpenGL ES for iOS, A Hands-on Guide to Modern 3D Graphics Programming.