华山论剑之浅谈iOS的宏定义以及内联函数的使用
你带着一个故事去听一首歌感觉会不一样,你带着一个故事去找一首歌心情会不一样。
宏定义的简介
宏定义是C提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译.
1. 不带参数的宏定义:
格式: #define 标识符 字符串
说明:
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域
(7)宏定义可以嵌套
(8)字符串" "中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
2. 带参数的宏定义:
除了一般的字符串替换,还要做参数代换
格式: #define 宏名(参数表) 字符串
例如:#define S(a,b) a*b
(1)实参如果是表达式容易出问题
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解
(4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)函数只有一个返回值,利用宏则可以设法得到多个值
(7)宏展开使源程序变长,函数调用不会
(8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)
iOS中常用的宏定义
上面说到宏定义主要分为参数宏和非参数宏,接下来,我们就看一下,在iOS开发过程中我们常用的一些宏定义.
-
尺寸相关
//主屏幕的宽高
#define KmainHight [UIScreen mainScreen].bounds.size.height
#define KmainWidth [UIScreen mainScreen].bounds.size.width
//导航栏高度
#define KnavigationBarHeight (44)
//标签栏高度
#define KtabBarHeight (49)
-
颜色相关
//普通颜色
#define KmyColor(R,G,B) [UIColor colorWithRed:R/255.0 green:G/255.0 blue:B/255.0 alpha:1.0]
// 随机颜色
#define KrandomColor [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1]
-
语言相关
//获取当前语言
#define KcurrentLanguage ([[NSLocale preferredLanguages] objectAtIndex:0])
-
系统版本相关
//主要用于判断当前iOS版本号,对其弃用或者未出现的方法进行区别对待
#define KcurrentSystemVersion [[UIDevice currentDevice] systemVersion]
-
手机型号相关
//iPhone5
#define IPHONE5 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(640, 1136), [[UIScreen mainScreen] currentMode].size) : NO)
//iPhone6
#define IPHONE6 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(750, 1334), [[UIScreen mainScreen] currentMode].size) : NO)
//iPhone6 Plus
#define IPHONE6_Plus ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2208), [[UIScreen mainScreen] currentMode].size) : NO)
//Pad
#define KisPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
-
模拟器相关
/判断是真机还是模拟器
#if TARGET_OS_IPHONE
//真机
#endif
#if TARGET_IPHONE_SIMULATOR
//模拟器
#endif
-
国际化相关(国际化相关知识不懂点这里)
#define LocalString(string) NSLocalizedString(string, nil)
-
引用相关
//弱引用
#define KweakSelf(type) __weak typeof(type) weak##type = type;
//强引用
#define KstrongSelf(type) __strong typeof(type) type = weak##type;
-
沙盒相关
//获取沙盒主路径
#define KhomePath NSHomeDirectory()
//获取沙盒 Temp
#define KtempPath NSTemporaryDirectory()
//获取沙盒 Document
#define KdocumentPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
//获取沙盒 Cache
#define KcachePath [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]
-
打印相关
//对象打印
#define KobjectLog(object) NSLog(@"%@",(object));
//整型数字打印
#define KintNumberLog(number) NSLog(@"%d",(number));
//float数字打印
#define KfloatNumberLog(number) NSLog(@"%f",(number));
-
上线相关
// 自定义NSLog,在debug模式下打印,在release模式下取消一切NSLog(上线时候使用推荐:⭐️⭐️⭐️⭐️⭐️)
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(FORMAT, ...) nil
#endif
内联函数的简介及其使用
当第一次接触内联函数的时候,我认为内敛函数就是宏定义,后来经过度娘的循循教导,我才知道对于内联函数原来是另有乾坤呀.
为什么要使用内联函数呢?其实主要是宏定义虽然简单易懂,但是容易出错,下面我们就举例子说明.
比如下面的这个宏定义,其实很简单,就是比较两个数的大小.
#define Kmax(a, b) (a) > (b) ? (a) : (b)
然后,我们这样调用
int result = Kmax(i, j) + 2 ;
其实结果就成了
result = (i) > (j) ? (i) : (j) + 2 ;
而我们需要的是
result = ( (i) > (j) ? (i) : (j) ) + 2 ;
这样就离我们需要的期望隔着失之毫厘谬以千里....好了,这时候有人就说了,如下改动不就没有任何问题了?但是真的没有问题了吗?
#define Kmax(a, b) ((a) > (b) ? (a) : (b))
如上的修改的确可以修改上面的问题,但是我们如下使用,还是存在问题的
result = Kmax(i++, j);
那么预处理会处理成如下,跟我们的预想的结果还是不一致...
result = (i++) > (j) ? (i++) : (j);
看到上面的问题这么多,相信你们的头和我一样是大大的,那么这时候就我们的猪脚--->内联函数来解决问题了,我们先看一下内联函数的结构.
NS_INLINE (函数类型,如void,int,id) 函数名 ( 参数a,参数b,..... ) {
..........(函数实现)..........
}
就拿上面的比较方法,我们可以直接写在PCH文件中.代码如下.(注意:NS_INLINE是内联函数的标志,同时要使用内联函数,需要导入Foundation框架)这样就完美的解决了上面的问题.
#import <Foundation/Foundation.h>
NS_INLINE int max( int a ,int b){
return a>b?a:b;
}
下面接着是分享给大家的一个内联函数的应用,一个自定义内联函数弹窗.(注意:因为UIAlertView被弃用的问题,所以会爆一个黄,但是真的很好用,所以就分享一下)
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
NS_INLINE void tipWithMessage(NSString *message){
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil];
[alerView show];
[alerView performSelector:@selector(dismissWithClickedButtonIndex:animated:) withObject:@[@0, @1] afterDelay:0.9];
});
}
下面对于调用也是非常的简单,如下在-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 方法中,调用弹窗函数.
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
tipWithMessage(@"世界,你好!");
}
下面是效果图.
内联函数的注意事项
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联:
-
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
-
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。