#import,#include,@import和断言
一、#import,#include,@import
关于Objective-C, Modules和Autolinking
OC自从Apple接手后,一直在不断改进。随着移动开发带来的OC开发者井喷式增加,客观上也要求Apple需要提供各种良好特性来支持这样一个庞大的开发者社区。iOS4时代的GCD, iOS5时代的ARC, iOS6时代的各种简化,每年我们都能看到OC在成为一种先进语言上的努力。基于Salalk和runtime,本身是C的超集,如此“根正苗红”的一门语言,在今年也迎来的新的变化。OC的最大变化就是加入了Modules和Autolinking。
1.1 什么是Modules ?
在了解Modules之前我们需要先了解一下OC的import机制 。#import<Frameworkoo/HeaderBar.h>,我相信每个开发者都写过这样的代码,用来引用其他的头文件。熟悉C或者C++的童鞋可能会知道,在C和C++里是没有#import的,只有#include (虽然GCC现在为C和C+ +做了特殊处理使得import可以被编译),用来包含头文件。#include做的事情其实就是简单的复制粘贴,将目标.h文件中的内容一字不落地拷贝到当前文件中,并替换掉这句include,而#import实质上做的事情和#include是一样的,只不过OC为了避免重复引用可能带来的编译错误(这种情况在引用关系复杂的时候很可能发生,比如B和C都引用了A, D又同时引用了B和C,这样A中定义的东西就在D中被定义了两次,重复了),而加入了#import,从而保证每个头文件只会被引用一次。
如果想深究,import的实现是通过ifndef一个标志进行判断,然后在引入后#define这个标志,来避免重复引用的
实质上import也还是拷贝粘贴,这样就带来一个问题:当引用关系很复杂,或者一个头文件被非常多的实现文件引用时,编译时引用所点的代码量就会大幅上升(因为被引用的头文件在各个地方都被copy了一遍)。为了解决这个问题,C系语言引入了预编译头文件(PreCompiled Header), 将公用的头文件放入预编译头文件中预先进行编译,然后在真正编译工程时再将预先编译好的产物加入到所有待编译的Source中去,来加快编译速度。比如iOS开发中Supporting Files组内的.pch文件就是一个预编译头文件, 默认情况下,它引用了UIKit和Foundation两个头文件 -- 这是在iOS开发中基本每个实现文件都会用到的东西。
于是理论上说,想要提高编译速度,可以把所有头文件引用都放到pch中。但是这样面临的问题是在工程中随处可用本来不应该能访问的东西,而编译器也无法准确给出错误或者警告,无形中增加了出错的可能性。
于是Modules诞生了。Modules相当 于将框架进行了封装,然后加入在实际编译之时加入了一个用来存放已编译添加过的Modules列表。如果在编译的文件中引用到某个Modules的话,将首先在这个列表内查找,找到的话说明已经被加载过则直接使用已有的,如果没有找到,则把引用的头文件编译后加入到这个表中。这样被引用到的Modules只会被编译一次,但是在开发时又不会被意外使用到,从而同时解决了编译时间和引用泛滥两方面的问题。
稍微追根问底,Modules是什么? 其实无非是对框架进行了如下封装,拿UIKit为例
framework module UIKit {
umbrella header "UIKit.h"
module *{export *}
link franework "UIKit"
}
这个Module定义了首要头文件(UlKit.h) ,需要导出的子modules (所有), 以及需要link的框架名称(UIKit) 。需要指出的是,现在Module还不支持第三方的框架,所以只有SDK内置的框架能够从这个特性中受益。另外,在C++的源代码中,Modules也是 被禁用的。
1.2 如何使用Module?
关于普通开发者使用的这个新特性的方法,Apple在LLVM5.0 (也就是Xcode5带的最新的编译器前端中)引入了一个新的编译符号@import,使用@符号将告诉编译器去使用Modules的引用形式,从而获取好处,比如想引用MessageUIl, 可以写成
@import MessageUI;
在使用上,这将等价于以前的#import <MessageUI/MessageUI.h> ,但是将使用Modules的特性。 如果只想使用某个特性的.h文件,比如#import <MessageUI/MFMailComposeViewController.h> ,对应写作@import PessageuI.MFMailComposeViecController;
当然,如果对于以前的工程,想要使用新的Modules特性, 如果要把所有头文件都这样一个一个改成@import的话,会是很大的一个工作量。Apple自然也考虑到了这一点, 于是对于原来的代码,只要使用的是iOS7或 者MacOS10.9的SDK, 在Build Settings中将Enable Modules(C and Objective-C)打开,然后保持原来的#import写法就行了。是的,不需要任何代码上的改变,编译器会在编译的时候自动地把可能的地方换成Modules的写法去编译的。
Autolinking是Modules的附赠小惊喜,因为在module定义的时候指定来link framework, 所以在编译module时LLVM会将所涉及到的框架自动帮你写到ink里去,不再需要到编译设置里去添加了。
二、断言
2.1 什么是断言?
●assert是断言、简单来讲、就是代码的作者认定这个条件一定要是能满足断言的条件、否则代码不满足最基本的条件也就不用再运行下去、防止错误的代码引起更深层的问题. 它的主要作用, 就是让开发者比较便捷的捕获一个错误, 让程序崩溃. 同时提出错误提示.
●NSAssert()是一个宏,用于开发阶段调试程序中的Bug, 通过为NSAssert()传递条件表达式来断定是否属于Bug, 满足条件返回真值,程序继续运行,如果返回假值,则抛出异常,并且可以自定义异常描述。
●NSAssert()是这样定义的:
#define NSAssert(condition, desc)
●condition是条件表达式, 值为YES或NO; desc为异常描述,通常为NSString。当conditon为YES时程序继续运行,为NO时,则抛出带有desc描述的异常信息。NSAssert0可以出现在程序的任何一个位置
2.2 NSAsserte和assert区别
NSAssert和assert都是断言,主要的差别是assert在断言失败的时候只是简单的终止程序, 而NSAssert会报告出错误信息并且打印出来. 所以只使用NSAssert就好, 可以不去使用assert。
2.3 NSAssert / NSCAssert
●iOS中用的最多的是两对断言, NSAssert/NSCAssert和NSParameterAssert/NSCparameterAssert.
●前者是适合于ObjectC的方法, cmd和self与运行时有关. 后者是适用于C的函数。
NSParameterAssert/NSCparameterAssert兩者的区別也是前者迺用于Objective-C的方法后者迺用于C的函数。实际开发中就用前者就可以了。
●NSAssert/NSCAssert 和 NSParameterAssert/NSCparameterAssert的区別是前者是针对条件断言,后者只是针对参数是否存在的断言, 调试时可以結合使用, 先判断参数,再迸一歩断言,确认原因.
3.4 NSAssert的用法
●运行则会崩溃并在控制台输出信息如下
2.5 NSParameterAssert用法
●运行则会崩溃并在控制台输出信息如下
2.6 断言的关闭
NSAssert也是一个预处理指令, 如果使用过多,也会影响你的程序运行, 这时我们要像在发布版本时处理NSLog一样处理这个预处理指令, 只不过他的处理方式有些不同
●首先进入项目工程文件
●选择Build Settings