iOS:学会使用宏,让工程更加高大上

我就是高大上
我就是高大上

写在前面

好像有一段时间没有更新了,年前项目期很忙,又赶上了公司被盗,电脑在公司丢了,算了想想都是泪,过年时候也忘了更,好吧,以后算是给自己一个任务,尽量一周更一篇,废话不多说直奔主题。

宏的使用

有时候我们在研究一些开源三方库的源码时会发现,const常量文件或者.h pch文件里会加入一些自己定义的宏,当然这也是必不可少的,一个库也好,项目也好,因为有让调用起来更简单,更高大上。

在宏中进行条件判断一般用#if 结束时#endif
例如我们最常见的控制台调试

#ifdef DEBUG
#define NSLog(format, ...) printf("\n[%s] %s [第%d行] %s\n", __TIME__, __FUNCTION__, __LINE__, [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define NSLog(format, ...)
#endif

好的,你也许会发现MJconst文件里是长这个样子的:

// 过期
#define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)

// 构建错误
#define MJExtensionBuildError(clazz, msg) \
NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \
[clazz setMj_error:error];

// 日志输出
#ifdef DEBUG
#define MJExtensionLog(...) NSLog(__VA_ARGS__)
#else
#define MJExtensionLog(...)
#endif

/**
 * 断言
 * @param condition   条件
 * @param returnValue 返回值
 */
#define MJExtensionAssertError(condition, returnValue, clazz, msg) \
[clazz setMj_error:nil]; \
if ((condition) == NO) { \
    MJExtensionBuildError(clazz, msg); \
    return returnValue;\
}

调用起来是这个样子的:

调用起来

就像上面MJExtensionDeprecated实际上是一个用来提示已废弃

我们来剖析一下上面定义宏的形式,很基础,希望帮助那些还在进步的coder们大神现在可以撤退啦

#define MJExtensionDeprecated(instead)  NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)

我们发现除了可以定义一些静态的常量外,也是可以传参的,在上面instead就被作为参数传到了后面,NS_DEPRECATED是系统宏 :

iOS:学会使用宏,让工程更加高大上_第1张图片
系统宏

NS_DEPRECATEDNS_AVAILABLE这两个宏很常见,NS_DEPRECATED表示在什么版本以后被废除,其中四个参数分别表示mac os的版本以后废除的描述iOS版本以后废除的描述NS_AVAILABLE宏则表示什么版本以后可用。
想必看到这就已经知道不再畏惧成堆的系统宏了,奥,原来MJExtensionDeprecated只是为了方便调用又重新定义了一遍,原来完全可以用:

说到这里既然也可以传参去使用,那么我们是不是一下子想到了很多?
譬如:

//对于frame的定义
#define Frame(x, y, width, height) CGRectMake((x),(y),(width),(height))

//对于size的定义
#define Size(width, height) CGSizeMake((width), (height))

//针对于iphone6尺寸的实际高度和宽度
#define HeightScale_IOS6(height) ((height/667.0) * Screen_height)
#define WidthScale_IOS6(width) ((width/375.0) * Screen_width)

//对于字体的定义:
#define font(size) [UIFont systemFontOfSize:(size)]

等等,只要你觉得方便的都可以去定义,甚至于我们还可以利用的特性去将一大串代码缩减至一句话(敲黑板了):
譬如MJ的状态检查,当需要在很多方法内调用相同的代码时大可以去宏定义一个:

// 状态检查
#define MJRefreshCheckState \
MJRefreshState oldState = self.state; \
if (state == oldState) return; \
[super setState:state];
iOS:学会使用宏,让工程更加高大上_第2张图片
调用demo

调用起来就一句话,如果这里有萌新,提示一下'\' 是换行符 不加入宏定义逻辑内。

我们甚至可以去定义一些烦恼的长代码段,例如定义一个单例(这里的##双井号是连接符,意思是将class类名与shared进行拼接,比如当classCat时,wyh_singleton_interface(Cat)定义的就是+(instancetype)sharedCat):

//.h
#define wyh_singleton_interface(class) + (instancetype)shared##class;

//.m
#define wyh_singleton_implementation(class) \
static class *_instance; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
\
return _instance; \
} \
\
+ (instancetype)shared##class \
{ \
if (_instance == nil) { \
_instance = [[class alloc] init]; \
} \
\
return _instance; \
}

在类的.h中就一行代码搞定~:


#import 

@interface Person : NSObject

wyh_singleton_interface(Person)


@end

#import "Person.h"

@implementation Person

wyh_singleton_implementation(Person)


@end

这样写完后,你便可以这样去创建一个Person的单例类:


- (void)viewDidLoad {
    [super viewDidLoad];

    Person *per = [Person sharedPerson];
}

是不是很简单,很神奇,很高大上,,我们再举几个例子:

NSAssert一般大神们都习惯去用的debug模式断言宏,这个宏是系统宏与NSCAssert差不多只不过一个OC用一个C用,想进一步了解一下这个宏的放传送门

戳死我呀~

iOS:学会使用宏,让工程更加高大上_第3张图片
示例

经过上面的介绍想必我们已经可以看懂上面的系统宏定义,NSAssert(condition, desc, ...)需要传入两个参数conditiondesc,其中condition是条件判断,desc是如果不成立的描述。

我们分析一下,当!(condition)也就是condition == NO的时候(即不成立的时候),NSAssertionHandler这个类的实例一般由系统自动创建,用于评估处理错误断言,如果NSAssert条件评估为NO,会向这个NSAssertionHandler实例发送一个错误描述desc,并且执行handleFailureInMethod:方法。

简单的来讲,就是当condition == NO时,程序会抛出异常,并在控制台打印错误信息。
好了到这就明白了,为什么大神都喜欢用NSAssert去断言,下面放个例子:

- (void)viewDidLoad {

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSString *str = @"我是梁朝伟";
    
    NSAssert([str isEqualToString:@"我是刘德华"], @"当str是刘德华时才能享受待遇");

}

这时运行程序,我们会发现程序崩溃:

iOS:学会使用宏,让工程更加高大上_第4张图片
示例2

好了 现在是不是很清楚宏的作用了,那是不是我们项目中把所有常量都交给宏,定义一大堆宏就逼格够高啦?

当然不是,因为如果一个项目过多的使用宏定义会增加程序预编译的时间,(因为宏是每次初始化都执行一次)这对debug调试来说无疑是不想看到的,所以一些静态常量就交给const修饰的常量吧(const常量初始化后只执行一次) ,这样就节约了程序运行的时间,普及一下const常量定义方法。

//.h中声明定义
UIKIT_EXTERN NSString * const HWPayReqCode;       

UIKIT_EXTERN NSString * const HWHttpRequestCache; 

UIKIT_EXTERN NSString * const HWLogoIconUrl;

//.m中实现定义

NSString * const HWPayReqCode = @"HWPayReqCode"; 

NSString * const HWHttpRequestCache = @"HWHttpRequestCache";

NSString * const HWLogoIconUrl = @"https://baidu.com";

最后,合理利用宏和静态常量使得项目中的全局调用变得更方便更有序,这篇文章就是抽空写一点无任何难度但容易被人忽略的问题,希望大家赶紧get起来去定义属于你自己项目里的高大上的吧。

你可能感兴趣的:(iOS:学会使用宏,让工程更加高大上)