写在前面
好像有一段时间没有更新简书了,年前项目期很忙,又赶上了公司被盗,电脑在公司丢了,算了想想都是泪,过年时候也忘了更,好吧,以后算是给自己一个任务,尽量一周更一篇,废话不多说直奔主题。
宏的使用
有时候我们在研究一些开源三方库的源码时会发现,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
好的,你也许会发现MJ
的const
文件里是长这个样子的:
// 过期
#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
是系统宏 :
NS_DEPRECATED
和NS_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];
调用起来就一句话,如果这里有萌新,提示一下'\'
是换行符 不加入宏定义逻辑内。
我们甚至可以去定义一些烦恼的长代码段,例如定义一个单例(这里的##
双井号是连接符,意思是将class
类名与shared
进行拼接,比如当class
是Cat
时,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 <Foundation/Foundation.h>
@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用,想进一步了解一下这个宏的放传送门
:
经过上面的介绍想必我们已经可以看懂上面的系统宏定义,NSAssert(condition, desc, ...)
需要传入两个参数condition
和desc
,其中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是刘德华时才能享受待遇");
}
这时运行程序,我们会发现程序崩溃:
好了 现在是不是很清楚宏的作用了,那是不是我们项目中把所有常量都交给宏,定义一大堆宏就逼格够高啦?
当然不是,因为如果一个项目过多的使用宏定义
会增加程序预编译的时间,(因为宏是每次初始化都执行一次)这对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起来去定义属于你自己项目里的高大上的宏
吧。