既然选择了远方,便只顾风雨兼程。

宏(define)

一. 宏的理解

  宏是一种批量处理的称谓。一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。这种替换在预编译时进行,称作宏展开。编译器会在编译前扫描代码,如果遇到我们已经定义好的宏那么就会进行代码替换,宏只会在内存中copy一份,然后全局替换,宏一般分为对象宏和函数宏。

  宏的优点:

  • 提高了程序的可读性,同时也方便进行修改,用户只需要在一处定义,多处使用,修改也只需要修改一处;

  宏的缺点:

  • 只在预处理里做文本替换,没有类型,不做类型检查。大量使用宏会导致二进制文件变大,会使编译时间变长;

二. 对象宏

  语法示例: #define M_PI 3.141592,项目中常用的对象宏如下所示。

  1. #ifndef HBObjectMacro_h
  2. #define HBObjectMacro_h
  3. // 获取iOS版本号
  4. #define kIOSVersions [[[UIDevice currentDevice] systemVersion] floatValue]
  5. // 获取window
  6. #define kUIWindow [[[UIApplication sharedApplication] delegate] window]
  7. // 获取屏幕宽
  8. #define kSCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
  9. // 获取屏幕高
  10. #define kSCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
  11. // 获取状态栏高度(20、44)
  12. #define kHeight_StatusBar [[UIApplication sharedApplication] statusBarFrame].size.height
  13. // 获取导航栏高度
  14. #define kHeight_NavigationBar 44.f
  15. // 获取导航栏加状态栏高度
  16. #define kHeight_NavBar (kHeight_StatusBar > 20?88.f:64.f)
  17. // 获取非安全区高度
  18. #define kHeight_NoSafeArea (kHeight_StatusBar > 20?34.f:0.f)
  19. // 获取Tabbar高度加非安全区高度
  20. #define kHeight_Tabbar (kHeight_StatusBar > 20?83.f:49.f)
  21. // 判断是否为iPhone
  22. #define IS_IPHONE ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
  23. // 判断是否为iPad
  24. #define IS_IPAD ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
  25. // 判断是否为iPod
  26. #define IS_IPOD ([[[UIDevice currentDevice] model] isEqualToString: @"iPod touch"])
  27. // 弱引用__weak
  28. #define kWeakObj(weakName,objName) __weak typeof(&*objName)weakName = objName;
  29. // 强引用__strong
  30. #define kStrongObj(strongName,objName) __strong typeof(&*objName)strongName = objName;
  31. // 数据持久化
  32. #define kUserDefaults [NSUserDefaults standardUserDefaults]
  33. // 通知中心
  34. #define kNotificationCenter [NSNotificationCenter defaultCenter]
  35. // 设置图片基础方法
  36. #define kUIImage(imageName) [UIImage imageNamed:imageName]
  37. // 设置字体基础方法
  38. #define kUIFont(size) [UIFont systemFontOfSize:size]
  39. // 设置字体基础方法
  40. #define kUIBoldFont(size) [UIFont boldSystemFontOfSize:size]
  41. // 设置随机颜色
  42. #define kRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]
  43. // 设置RGB颜色
  44. #define kRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
  45. // 设置RGBA颜色
  46. #define kRGBAColor(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(r)/255.0 blue:(r)/255.0 alpha:a]
  47. // 这是clear背景颜色
  48. #define kClearColor [UIColor clearColor]
  49. #endif /* HBObjectMacro_h */

三. 函数宏

  函数宏的作用就类似于一个函数一样,函数一般代码量比较多,这时如果函数宏过长,我们可以使用\进行换行处理,这样提升了代码的可读性。

  1. #define IS_LEAP_YEAR2(y) y%4==0&&y%100!=0 \
  2. ||y%400==0

四. 预编译指令和运算符

  接下来我们了解下宏定义中常用的预编译指令和运算符。

4.1 预编译指令

  1. # 空指令,无任何效果
  2. #define 定义宏
  3. #undef 取消已定义的宏
  4. #if 如果给定条件为真,则编译下面代码
  5. #ifdef 如果宏已经定义,则编译下面代码
  6. #ifndef 如果宏没有定义,则编译下面代码
  7. #elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
  8. #endif 结束一个#if……#else条件编译块
  9. #error 停止编译并显示错误信息

4.2 常用运算符

  • \换行处理。

  • #运算符:在OC中使用字符串都需要使用@"",如果想直接使用字符串可以添加一个#运算符。出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。

    1. # define Test(n) "Test"#n
    2. // 调用:
    3. Test(Demo); // 打印:TestDemo
  • ##运算符:用于将相邻的两个标记(Token)连接为一个。使用时先分隔(根据空格或其他操作分隔符[+,-,*,/,”,”等]),再强制连接(去掉和前面的字符串间的空格,再连接起来)。

    1. #define Data(a, b, c) a##b##c
    2. NSLog(@"%d", Data(1, 2, 3)); // 123
  • @# :字符化操作符,只能用于有参数传入的宏定义中,必须置于宏定义体参数名前,作用是将传入的单字符参数名转换成字符,以一对单引号括起来。

五. 预定义宏和可变参数宏

5.1 C语言中预定义宏

  1. __FILE__ :当前源代码的文件名(字符串)
  2. __LINE__:当前源代码中的行号(整型)
  3. __DATE__:进行预处理的日期(”Mmm dd yyyy”形式的字符串)
  4. __TIME__:源文件编译时间(格式“hh:mm:ss”)
  5. __FUNCTION__:同__func__(但IDE不支持),当前源代码的函数名
  6. __PRETTY_FUNCTION__:同__FUNCITON__,但在g++下会输类名、函数名及其他函数信息
  7. 例:

5.2. 可变参数宏

  可变参数宏:#define DBGMSG(format, ...) fprintf (stderr, format, __VA_ARGS__) 表示一个可变化的参数表,变参必须放于最后一个参数;

  __VA_ARGS__:是一个可变参数的宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错;

  1. #ifdef DEBUG
  2. #define NSLog(format, ...) NSLog(@"%s(%d): " format, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
  3. #else
  4. #define NSLog(...)
  5. #endif

常量(const)

  在开发中,当我们想定义全局共用的一些数据时,比如通知名字,服务器的地址等等,我们经常使用用到宏定义、变量,或者用const常量来修饰这些数据类型。然而经常有开发者不知道怎么正确使用这些修饰符,导致项目中乱用宏定义与const修饰符。那么我们该怎么正确选择使用呢?参考苹果API的使用,建议尽量使用const来定义,为什么要这么做呢?下面我们先从基本概念方面来了解下他们之间的联系和区别。

  • 宏:用法,一般字符串抽成宏,代码抽成宏使用。宏只是在预处理阶段进行文本替换,没有类型,不做任何类型检查,编译器可以对相同的字符串进行优化,只保存一份到数据段。甚至有相同后缀的字符串也可以优化,你可以使用GCC编译测试,Hello worldworld两个字符串,只存储前面一个。取的时候只需要给前面和中间的地址,如果是整型、浮点型会有多分拷贝,但这些数写在指令中,占的只是代码片段而且,大量使用宏会导致二进制文件变大。

    1. #define URL @"http://www.xx.xx"
  • 变量:共享一块内存空间,就算项目中多处用到,也不会分配多块内存空间,可以被修改,在编译阶段做类型检查。

    1. NSString *url = @"http://www.xx.xx";
  • 常量:共享一块内存空间,就算项目中多处用到,也不会分配多块内存空间,可以根据const修饰的位置设定能够修改,在编译阶段做类型检查。一般常用的字符串定义成const(对于常量字符串苹果推荐我们使用const)。

    • 常量区分:

      • 全局常量:不管你定义在任何文件夹,外部都能访问;

        1. // ViewController.m文件中 定义全局常量
        2. const NSString *url = @"http://www.xx.xx";
        3. // AppDelegate.m文件中 访问全局变量
        4. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        5. // Override point for customization after application launch.
        6. // 访问全局常量
        7. extern NSString *url;
        8. NSLog(@"008 - 宏定义 - 访问全局常量 = %@",url);
        9. return YES;
        10. }
        11. // 输出:2021-09-06 15:44:20.151408+0800 BaseGrammar[95798:357990] 008 - 宏定义 - 访问全局常量 = http://www.xx.xx
      • 局部常量:用static修饰后,不能供外界访问;

        1. static const NSString *url = @"http://www.xx.xx";
    • const位置不同,代表什么:

      1. // 写法一:*url不能被修改,url能被修改
      2. const NSString *url = @"http://www.xx.xx";
      3. // 写法二:*url不能被修改,url能被修改
      4. NSString const *url = @"http://www.xx.xx";
      5. // 写法三:url不能被修改,*url能被修改
      6. NSString * const url = @"http://www.xx.xx";

        由以上代码我们可以总结为:const右边的总不能被修改。所以我们一般定义一个常量又不想被修改,应该这样定义:

      1. NSString * const url = @"http://www.xx.xx";
    • 常量的规范使用,一般项目里,定义全局常量,会写在独立文件里:

      1. // HBConst.m文件定义常量
      2. #import "HBConst.h"
      3. // 定义常量
      4. /** 网络请求地址*/
      5. NSString * const HBUrl = @"http://www.xx.xx";
      6. /** cell间距 */
      7. CGFloat const HBCellMargin = 4.0;
      1. // HBConst.h文件提供外接访问常量
      2. /** 网络请求地址*/
      3. UIKIT_EXTERN NSString * const HBUrl;
      4. /** cell间距 */
      5. UIKIT_EXTERN CGFloat const HBCellMargin;

    宏与const区别:

  1. 编译时刻不同,宏(define)属于预编译,在预处理阶段进行替换;const常量在编译阶段使用;
  2. (define)可以定义代码(如函数),const不可以;
  3. (define)不做类型检查,只进行替换,const常量有数据类型,会执行类型检查;
  4. (define)定义的常量在替换后运行过程中,会不断占用内存,而const定义的常量存储在数据段,只有一份拷贝,效率更高;

  最后建议,我们以后在开发中如果定义一个常量字符串就用const,定义代码就用宏。

宏(define)与常量(const)的更多相关文章

  1. 宏(define)与常量(const)

    http://mp.weixin.qq.com/s?__biz=MzAxMzE2Mjc2Ng==&mid=402470669&idx=1&sn=e34db91190d8d46f ...

  2. iOS学习——iOS 宏(define)与常量(const)的正确使用

    概述 在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与const修饰.你能区分下面的吗?知道什么时候用吗? #define HSCode ...

  3. iOS 宏(define)与常量(const)的正确使用

    在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与const修饰 你能区分下面的吗?知道什么时候用吗? #define HSCoder @ ...

  4. 【转】iOS 宏(define)与常量(const)的正确使用-- 不错

    原文网址:http://www.jianshu.com/p/f83335e036b5 在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与c ...

  5. #define 和常量 const 的区别

    const 后的常量,程序对其中只能读不能修改. #include <iostream> using namespace std; int main() { const double pi ...

  6. _编程语言_C++_宏定义#define 和 常量const 的区别

    C++中有两种定义常量的方式:#define预处理和const关键字 #define 预处理指令 #include <iostream> using namespace std; #def ...

  7. OC中修饰符:宏define 常量:const extern

    const const最好理解,修饰的东西不能被修改 指针类型根据位置的不同可以理解成3种情况: I 常量指针 // 初始化之后不能赋值,指向的对象可以是任意对象,对象可变. NSString * c ...

  8. const与预处理宏#define的区别

    在c语言程序设计时,预处理器可以不受限制地建立宏并用它来替代值.因为预处理器只做一些文本替换,宏没有类型检测概念,也没有类型检测功能.所以预处理器的值替换会出现一些小的问题,出现的这些问题,在c++中 ...

  9. 为什么很多人使用#define而不是const定义常量

    众所周知,C语言一开始只有#define,C程序员用#define定义符号常量.但后来ANSI C加入了const限定符,而const应该比#define更好,为什么现在的C程序员还在大量使用#def ...

  10. PHP中const和define()定义常量的细节区别

    转自:http://www.365mini.com/page/difference-of-define-and-const.htm 众所周知,在PHP中(php 4及以后),我们可以使用函数defin ...

随机推荐

  1. React: 按钮点击时修改颜色

    背景 当存在多个点击按钮时,需要提示用户点击的哪个按钮,所以要进行颜色的修改 import * as React from 'react'; import './style.css'; export ...

  2. quarkus依赖注入之八:装饰器(Decorator)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<quarkus依赖注入> ...

  3. 分布式存储系统举例剖析(elasticsearch,kafka,redis-cluster)

    1. 概述 对于分布式系统,人们首先对现实中的分布式系统进行高层抽象,然后做出各种假设,发展了诸如CAP, FLP 等理论,提出了很多一致性模型,Paxos 是其中最璀璨的明珠.我们对分布式系统的时序 ...

  4. QA|4个数据打开了4个页面,怎么实现只打开一个页面?单例模式|网页计算器自动化测试实战

    如下图,代码中4个数据,产生了4个页面,怎么实现只打开一个页面?可使用单例模式 查询得知 单例模式实现有5种方法,参照链接下: https://blog.csdn.net/SixStar_FL/art ...

  5. Android 调试桥 (adb) 使用教程/示例

    sidebar: auto Android 调试桥 (adb) Android 调试桥 (adb) 是一种功能多样的命令行工具,可让您与设备进行通信.adb 命令可用于执行各种设备操作,例如安装和调试 ...

  6. windows下flutter的环境安装

    Flutter是谷歌出品的移动应用SDK,性能卓越.体验精美.跨平台.HotReload等等这些特点. Dart是谷歌推出的编程语言.支持即时编译JIT(Just In Time).HotReload ...

  7. Dami 基于事件总线的本地过程调用框架(首次发版)

    Dami,专为本地多模块之间通讯解耦而设计(尤其是未知模块.隔离模块.领域模块).零依赖,特适合 DDD. 特点 结合 Bus 与 RPC 的概念,可作事件分发,可作接口调用,可作异步响应. 支持事务 ...

  8. ModbusTCP 转 Profinet 主站网关控制汇川伺服驱动器配置案例

    ModbusTCP 转 Profinet 主站网关控制汇川伺服驱动器配置案例 ModbusTCP Client 通过 ModbusTCP 控制 Profinet 接口设备,Profinet 接口设备接 ...

  9. 如何在没有第三方.NET库源码的情况,调试第三库代码?

    大家好,我是沙漠尽头的狼. 本方首发于Dotnet9,介绍使用dnSpy调试第三方.NET库源码,行文目录: 安装dnSpy 编写示例程序 调试示例程序 调试.NET库原生方法 总结 1. 安装dnS ...

  10. Python+SVM

    # !/usr/bin/env python # encoding: utf-8 # SVM算法 支持向量机 from sklearn import svm import numpy as np fr ...