宏是一种批量处理的称谓,简单来讲就是根据定义好的规则替换必定的文本。替换过程在程序编译期,也所以大量使用宏会形成编译时间变长;并且替换过程不进行类型安全检查;还须要注意“边缘效应”;html
好比#define N 1 + 2
,使用时NSInteger a = N / 2
, 预期1.5
,结果是2
,由于在处理过程当中转化为NSInteger a = 1 + 2 / 2
,因此建议使用宏时加括号代表是一个总体。c++
想要了解宏的话得先了解一下来源,OC从C语言演变来,天然也继承了C语言的优良传统,这里简单介绍一下,C语言中预处理命令,它包括三个方面:数组
- 宏定义:#define 指令定义一个宏,#undef指令删除一个宏定义。
- 文件包含:#include指令指定一个文件的内容被包含到程序中。
- 条件编译:#if,#ifdef,#ifndef,#elif,#else和#endif指令能够根据编译器能够测试的条件来将一段文本包含到程序中或排除在程序以外。
须要注意的是预处理命令都是以符号“#”开头。安全
大部分将宏按类型分为对象宏和函数宏,也有按传入参数分为带参数的宏和不带参数的宏。bash
#define STATUS_HEIGHT 20 复制代码
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) 复制代码
#define
与const
均可用来修饰常量。markdown
将传入的单字符参数名转换成字符,以一对单引用括起来.app
#define STRING @#s // 's' 复制代码
在宏参数前加个#,那么在宏体扩展的时候,宏参数会被扩展成字符串的形式。dom
#define NSSTRING #str // "str" 复制代码
若是宏体所在标示符中有##,那么在宏体扩展的时候,宏参数会被直接替换到标示符中。ide
#define COMMAND(PREFIX, NAME) PREFIX##NAME 复制代码
遇到须要换行的能够用\号链接;函数
#define PRINT_IF(CONDITION) \ do { if (CONDITION) \ NSLog(@"print hello"); } \ while (0) 复制代码
…
和_VA_ARGS
)下面是OC中自定义Log的例子:
#ifdef DEBUG #define Log(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) #else #define Log(...) #endif 复制代码
须要注意的是: __VA_ARGS__
: 至少传一个参数 ##__VA_ARGS__
: 随便传几个参数
下面是一些C语言的宏:
#define 定义一个预处理宏 #undef 取消宏的定义 #include 包含文件命令 #include_next 与#include类似, 但它有着特殊的用途 #if 编译预处理中的条件命令, 至关于C语法中的if语句 #ifdef 判断某个宏是否被定义, 若已定义, 执行随后的语句 #ifndef 与#ifdef相反, 判断某个宏是否未被定义 #elif 若#if, #ifdef, #ifndef或前面的#elif条件不知足, 则执行#elif以后的语句, 至关于C语法中的else-if #else 与#if, #ifdef, #ifndef对应, 若这些条件不知足, 则执行#else以后的语句, 至关于C语法中的else #endif #if, #ifdef, #ifndef这些条件命令的结束标志. #defined 与#if, #elif配合使用, 判断某个宏是否被定义 #line 标志该语句所在的行号 # 将宏参数替代为以参数值为内容的字符窜常量 ## 将两个相邻的标记(token)链接为一个单独的标记 #pragma 说明编译器信息 #warning 显示编译警告信息 #error 显示编译错误信息 复制代码
是否是感受很熟悉,就算在iOS开发中也常常用到里面的内容。 除了基本的宏操做还有预约义宏,预约义宏是为了方便处理一些有用的信息,里面定义了一些预处理标识符,也就是预约义宏。预约义宏的名称都是以“__”(两条下划线)开头和结尾的,若是宏名是由两个单词组成,那么中间以“_”(一条下划线)进行链接。而且,宏名称通常都由大写字符组成。 下面是常见的预约义宏:
宏 | 描 述 |
---|---|
FUNTION | 获取当前函数名 |
DATE | 丐前源文件的编泽口期,用 “Mmm dd yyy”形式的字符串常量表示 |
FILE | 当前源文件的名称,用字符串常量表示 |
LINE | 当前源义件中的行号,用十进制整数常量表示,它能够随#line指令改变 |
TIME | 当前源文件的最新编译吋间,用“hh:mm:ss”形式的宁符串常量表示 |
STDC | 若是今前编泽器符合ISO标准,那么该宏的值为1,不然未定义 |
COUNTER | 无重复的计数器,从程序启动开始每次调用都会++,经常使用语宏中定义无重复的参数名称 |
func | 所在scope的函数名称,常见于log中 |
宏 | 描 述 |
---|---|
__has_include | 用来检查是否引入了某个文件 |
NS_ASSUME_NONNULL_BEGIN & NS_ASSUME_NONNULL_END | 在这两个宏之间的代码,全部简单指针对象都被假定为nonnull |
__cplusplus | 识别是c代码仍是c++代码 |
__has_feature(objc_arc) | 判断是不是ARC,不然为MRC |
@available(iOS 11, *) | 当前iOS11是否知足需求 |
TARGET_IPHONE_SIMULATOR | 知足条件时,执行模拟器代码;不然执行非模拟器代码 |
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 | 设备系统大于8.0 以上的代码 |
NS_REQUIRES_SUPER | 申明子类若是重写该方法,必须调用该父类方法 |
FOUNDATION_EXPORT | 用于定义常量,在检测值是否相等时直接比较指针,效率比较快 |
NS_AVAILABLE_IOS(8_0) | 这个方法能够在iOS3.0及之后的版本中使用,若是在比5.0更老的版本中调用这个方法,就会引发崩溃 |
NS_DEPRECATED_IOS(2_0, 6_0) | 这个方法在iOS2.0引入,6.0被删除 |
NS_AVAILABLE(10_8, 6_0) | 这个宏告诉咱们这方法分别随Mac OS 10.8和iOS 6.0被引入 |
NS_DEPRECATED(10_0, 10_6, 2_0, 4_0) | 这个方法随Mac OS 10.0和iOS 2.0被引入,在Mac OS 10.6和iOS 4.0后被废弃 |
NS_CLASS_AVAILABLE(10_11, 9_0) | 这个类分别随Mac OS 10.11和iOS9.0被引入 |
NS_ENUM_AVAILABLE(10_11, 9_0) | 这个枚举分别随Mac OS 10.11和iOS9.0被引入 |
__IPHONE_OS_VERSION_MAX_ALLOWED | 容许最大的iOS版本 |
__IPHONE_OS_VERSION_MIN_ALLOWED | 最低的iOS版本 |
/**** UI尺寸 ****/ //获取屏幕宽度与高度 #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width #define SCREENH_HEIGHT [UIScreen mainScreen].bounds.size.height //根据6,7,8适配 #define ScaleWidth(width) (width / 375.0) * SCREEN_WIDTH #define ScaleHeight(height) (height / 667.0) * SCREENH_HEIGHT //是不是iPhoneX #define k1IS_iPhoneX (SCREEN_WIDTH == 375.f && SCREENH_HEIGHT == 812.f) #define k2IS_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) //判断是否为X系列 #define IPHONE_X \ ({BOOL isPhoneX = NO;\ if (@available(iOS 11.0, *)) {\ isPhoneX = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;\ }\ // 状态栏高度 #define kStatusBarHeight (IPHONE_X ? 44.f : 20.f) // 顶部导航栏高度 #define kNavigationBarHeight 44.f // 顶部安全距离 #define kSafeAreaTopHeight (IPHONE_X ? 88.f : 64.f) // 底部安全距离 #define kSafeAreaBottomHeight (IPHONE_X ? 34.f : 0.f) // Tabbar高度 #define kTabbarHeight 49.f // 去除上下导航栏剩余中间视图高度 #define ContentHeight (kScreenHeight - kSafeAreaTopHeight - kSafeAreaBottomHeight - kTabbarHeight) /**** 颜色 ****/ //随机颜色 #define ZBRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0] //RGB #define ZBRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] //RGBA #define ZBRGBAColor(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(r)/255.0 blue:(r)/255.0 alpha:a] //十六进制颜色 #define ZBRGBHex(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] //十六进制颜色,透明度 #define ZBRGBHexAlpha(rgbValue,a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:(a)] /**** 系统相关 ****/ //app版本号 #define DEVICE_APP_VERSION (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] //app Build版本号 #define DEVICE_APP_BUILD (NSString *)[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] //系统版本号(string) #define DEVICE_OS_VERSION [[UIDevice currentDevice] systemVersion] //系统版本号(float) #define DEVICE_OS_VERSION_VALUE [DEVICE_OS_VERSION floatValue] //检测是不是竖屏状态 #define IsPortrait ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait || [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) /**** 沙盒目录文件 ****/ //temp #define ZBPathTemp NSTemporaryDirectory() //Document #define ZBPathDocument [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] //Cache #define ZBPathCache [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject] /**** 数据判空 ****/ //字符串是否为空 #define kStringIsEmpty(str) ([str isKindOfClass:[NSNull class]] || str == nil || [str length] < 1 ? YES : NO ) //数组是否为空 #define kArrayIsEmpty(array) (array == nil || [array isKindOfClass:[NSNull class]] || array.count == 0) //字典是否为空 #define kDictIsEmpty(dic) (dic == nil || [dic isKindOfClass:[NSNull class]] || dic.allKeys == 0) //是不是空对象 #define kObjectIsEmpty(_object) (_object == nil \ || [_object isKindOfClass:[NSNull class]] \ || ([_object respondsToSelector:@selector(length)] && [(NSData *)_object length] == 0) \ || ([_object respondsToSelector:@selector(count)] && [(NSArray *)_object count] == 0)) /**** 经常使用缩写 ****/ #define kApplication [UIApplication sharedApplication] #define kKeyWindow [UIApplication sharedApplication].keyWindow #define kAppDelegate [UIApplication sharedApplication].delegate #define kUserDefaults [NSUserDefaults standardUserDefaults] #define kNotifCenter [NSNotificationCenter defaultCenter] /**** 其余 ****/ //弱引用 #define ZBWeak __weak typeof(self) weakSelf = self; #define ZBWeakSelf(type) __weak typeof(type) weak##type = type; //强引用 #define ZBStrongSelf(type) __strong typeof(type) type = weak##type; //角度转换弧度 #define ZBDegreesToRadian(x) (M_PI * (x) / 180.0) //弧度转换角度 #define ZBRadianToDegrees(radian) (radian*180.0)/(M_PI) //block判空回调 #define ZBBlockNotEmpt(block, ...) if (block) { block(__VA_ARGS__); } //.h头文件中的单例宏 #define ZBSingletonH(name) + (instancetype)shared##name; //.m文件中的单例宏 #define ZBSingletonM(name) \ static id _instance;\ + (instancetype)allocWithZone:(struct _NSZone *)zone{\ static dispatch_once_t onceToken;\ dispatch_once(&onceToken, ^{\ _instance = [super allocWithZone:zone];\ });\ return _instance;\ }\ + (instancetype)shared##name{\ static dispatch_once_t onceToken;\ dispatch_once(&onceToken, ^{\ _instance = [[self alloc] init];\ });\ return _instance;\ }\ - (id)copyWithZone:(NSZone *)zone{\ return _instance;\ } 复制代码
介绍了这么多宏的相关知识,最后总结下对宏的几点感想:
- 宏直接调用方法名或者常量名称的方式易于理解,能够减小重复代码,统一规范,方便修改;
- 使用太多宏会增长编译时长,并且还需注意“边缘效应”,防止发生不可预期的错误;
- 定义宏时应遵照规范,好比宏名和参数的括号间不能有空格;定义表达式要外面用括号包裹等;
暂时先说这么多,后续还将继续更新,最后欢迎大佬们下方吹水。