华山论剑之浅谈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)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。


总结


关于iOS的宏定义以及内联函数的使用就说到这里,两者说起来各有利弊,但是不得不说的是有了宏定义以及内联函数之后,对开发是一个不少的助力.好了,今天就说到这里了,希望文章对您有所帮助.码字不易,跪求点赞..😄

推荐阅读更多精彩内容