本文详细介绍了宏定义的概念及其在Objective-C中的应用,包括宏定义的优缺点、边界效应、系统预编译宏、自定义宏等内容,并给出了实际使用的案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

宏优缺点

宏定义的优点:

  1. 提高了程序的可读性,同时也方便进行修改。
  2. 提高程序的运行效率。使用带参数的宏定义可完成函数调用的功能,又能减少系统开销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生函数调用时,需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。
  3. 宏是由预处理器处理的,通过字符串操作可以完成很多编译器无法实现的功能。比如##连接符。

宏定义的缺点:

  1. 无法对宏定义中的变量进行类型检查。
    此缺点,是相对于const变量来说的。define定义的变量,是Compile-Time时期的变量,系统在编译时候,就将其全部替换,而不会对其变量进行类型等属性检查,相对不是很安全,可能存在潜在的问题,而没有发现。正因为其仅仅是编译时期替换,所以其定义的变量,是不会在运行时候分配内存的,不占用内存空间。#define N 10 现在还没占用内存,int i = N; // 这时候要分配内存。const定义的变量,是 Run-Time时期的变量,如果类型不匹配,系统在运行时候,就会发现并提示或报错,对应的,const变量在运行时期,也是一种变量,系统会为其分配内存.

  2. 边界效应

    1. 未加括号带来的边界效应。
      由于宏定义的时候,其各个分量未加括号,而在使用宏定义的时候,传递的参数是变量的表达式,然后经过系统展开后,由于优先级的原因,导致其结果不是你所希望的。

      #define MUL(A,B) A*B
      int a=1,b=2,c=3,d=0;
      d=MUL(a+b,c)
      // 经过编译时候展开,就变成了
      d=a+b*c
      // 而不是我们所希望的
      d=(a+b)*c
      

      解决办法
      其解决办法也很简单,就是给每个分量,都加上括号,就可以避免此类问题。即在宏定义的时候,如此定义:

      #define MUL(A,B) ((A)*(B))
      
    2. 在define数据类型的时候, 未加括号带来的问题
      在用define进行新的数据类型定义的时候,由于未加括号,会出现你所未预料到的结果。此点其实就是上面说的边界效应,之所以将此点单独说一下,是由于此点不是普通计算结果的问题,而是数据类型的问题,问题相对更严重。

      #define dPS struct s *   //注意末尾无分号
      //当使用的时候,遇到:
      dPS p1,p2;
      的时候,经过编译时候替换扩展,就变成了
      struct s* p1,p2;
      而p2就不是我们所希望的s的指针类型了,而是一个普通的s数据结构类型的了.产生了边界效应.
      

      [解决办法]
      对应的解决办法也很简单,就是,遇到此类新数据类型定义的时候,还是用typedef
      将上述宏定义改为:
      typedef struct s * tPS; // 注意末尾有分号
      而后的使用:
      tPS p1,p2;
      就正常了.

    3. 特殊情况时候,加了括号也无法避免错误
      在宏定义中出现++或—-之类的操作符的时候,即使加括号,也无法避免其中的问题
      [例子]

      #define MIN(A,B) ((A)<(B)?(A):(B))
      
      

      如果进行如此调用
      int a=1,b=3,min=0;
      min=MIN(a++,b);
      经过编译替换展开后,就成了
      max=((a++)< (b)?(a++):(b))
      计算出来的结果,就是
      min=3,而不是我们所要的min=1了.

      此类问题无法避免,除非程序员在调用宏的时候,自己多加注意,尽量避免此类调用.

系统预编译宏

1.NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END

苹果在Xcode 6.3引入了一个Objective-C的新特性:nullability annotations。这一新特性的核心是两个新的类型注释:__nullable和__nonnull。从字面上我们可以猜到,__nullable表示对象可以是NULL或nil,而__nonnull表示对象不应该为空。当我们不遵循这一规则时,编译器就会给出警告,不过这只是一个警告,程序还是能编译通过并运行。
事实上,在任何可以使用const关键字的地方都可以使用__nullable和__nonnull,不过这两个关键字仅限于使用在指针类型上。而在方法的声明中,我们还可以使用不带下划线的nullable和nonnull,如下所示:

- (nullable id)itemWithName:(NSString * nonnull)name

在属性声明中,也增加了两个相应的特性,因此上例中的items属性可以如下声明:

@property (nonatomic, copy, nonnull) NSArray * items; 

如果需要每个属性或每个方法都去指定nonnull和nullable,是一件非常繁琐的事。苹果为了减轻我们的工作量,专门提供了两个宏:NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。在这两个宏之间的代码,所有简单指针对象都被假定为nonnull,因此我们只需要去指定那些nullable的指针。如下代码所示:

NS_ASSUME_NONNULL_BEGIN
@interface TestNullabilityClass () 
@property (nonatomic, copy) NSArray * items; 
- (id)itemWithName:(nullable NSString *)name; 
@end 
NS_ASSUME_NONNULL_END

在上面的代码中,items属性默认是nonnull的,itemWithName:方法的返回值也是nonnull,而参数是指定为nullable的。

2.UIKIT_EXTERN和FOUNDATION_EXTERN

UIKIT_EXTERN和FOUNDATION_EXTERN = extern
UIKIT_EXTERN NSString *const kBaseUrl;
extern NSString *const kBaseUrl;

3.NS_AVAILABLE_IOS、NS_DEPRECATED_IOS、NS_CLASS_AVAILABLE、NS_ENUM_AVAILABLE、NS_AVAILABLE、NS_DEPRECATED

NS_AVAILABLE_IOS(5_0)
这个方法可以在iOS5.0及以后的版本中使用,如果在比5.0更老的版本中调用这个方法,就会引起崩溃。
NS_DEPRECATED_IOS(2_0, 6_0)
这个宏中有两个版本号。前面一个表明了这个方法被引入时的iOS版本,后面一个表明它被废弃时的iOS版本。被废弃并不是指这个方法就不存在了,只是意味着我们应当开始考虑将相关代码迁移到新的API上去了。
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后被废弃。
类似于:TVOS中__TVOS_PROHIBITED不可用或者__TVOS_UNAVAILABLE不可用或者__TVOS_AVAILABLE(_vers)可用或者__TVOS_DEPRECATED(_start, _dep, _msg)废弃.

NS_CLASS_AVAILABLE(10_11, 9_0)

这个类分别随Mac OS 10.11和iOS9.0被引入。

NS_CLASS_AVAILABLE_IOS(6_0) 
@interface UICollectionViewFlowLayout : UICollectionViewLayout
@end
NS_ENUM_AVAILABLE(10_11, 9_0)

这个枚举分别随Mac OS 10.11和iOS9.0被引入。

4.__IPHONE_OS_VERSION_MIN_REQUIRED、__IPHONE_OS_VERSION_MAX_ALLOWED、__IPHONE_2_0~__IPHONE_10_2

__IPHONE_OS_VERSION_MIN_REQUIRED对应着Xcode deployment target的最低支持版本。
__IPHONE_OS_VERSION_MAX_REQUIRED对应着Xcode deployment target的最高支持版本。
IPHONE_2_0 iOS2.0;IPHONE_10_2 iOS10.2
使用

#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0)

#if (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 && defined(__IPHONE_7_0))
        if (iOS8_LATER) {
            _collectionFlowLayout.estimatedItemSize = CGSizeMake(50, 50);
        }
#else
        _collectionFlowLayout.estimatedItemSize = CGSizeMake(50, 50);
#endif

自定义宏

1.简单宏定义

#define N 100
#define S @"STRING"
#define BEGIN {
#define END }
#define MY_INT int // 对类型重命名
#define DEBUG 0 // 控制条件编译

常见错误:

#define N = 100
#define N 100;

2.带参数的宏

宏定义需要加括号的两种情况:
(1)如果宏的替换列表中带有运算符,那么使用要将替换列表放到括号中。例如#define MAX_VALUE(X,Y) ((X) > (Y) ? (X) : (Y))
(2)如果宏有参数,每次参数在替换列表中出现时都要放在括号中。同上

// 计算两个数中最大值

#define MAX_VALUE(X,Y) ((X) > (Y) ? (X) : (Y))

// 打印字典和数组

#define NSLOG_ARRAY_OR_DICT(ARRAY,DICT) (NSLog(@"array = %@,dict = %@",[(ARRAY) description],[(DICT) description]));

// 设计技巧:dowhile中出现;

#define NSLOG_ARRAY_OR_DICT2(ARRAY,DICT) do {int a = 1;NSLog(@"a = %d",a);NSLog(@"array = %@,dict = %@",[(ARRAY) description],[(DICT) description]);}while(0)

// 单例

#undef  ZB_SINGLETON_DEFINE
#define ZB_SINGLETON_DEFINE( __class ) \
+ (__class *)sharedInstance; 

3. #运算符和##运算符

(1)出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如:

#define PASTE(n) "adhfkj"#n
main()
{
printf("%s\n",PASTE(15));
}

宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。所以输出应该是adhfkj15。

针对Window,dos,os2不同的系统对WIDTH进行不同的定义
单独一行的#是空指令

#ifdef WINDOWS
#
#define WIDTH 375
#
#elif defined(DOS)
#
#define WIDTH 414
#
#elif defined(OS)
#
#define WIDTH 320
#
#else
#
//#error no sysytem;
#
#endif

(2)##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号。看下面的例子:

#define NUM(a,b,c) a##b##c
#define STR(a,b,c) a##b##c
main()
{
printf("%d\n",NUM(1,2,3));
printf("%s\n",STR("aa","bb","cc"));
}

最后程序的输出为:
123
aabbcc

#define IMAGE_NAME(NAME) @"image_name"#NAME  // IMAGE_NAME(3)=image_name3
#define STR(NAME,AGE,SEX) @"名字:"#NAME@".年龄:"#AGE@".性别:"#SEX // 名字:@“王五".年龄:24.性别:@"男"

3.取消宏定义

#undef NUM1

5.条件编译 args...表示有多个参数

打印信息

#define DEBUG 1
#if DEBUG
#define MY_NSLog(fmt,args...) NSLog(@fmt,##args)
#else
#define MY_NSLog(fmt,args...)
#endif

常用宏定义

ZBConst.h 宏定义文件集合(包含以下宏定义)==>
ZBHttpConst.h 网络相关宏定义(推荐用const常量代替)
ZBNotificationConst.h 通知相关宏定义(推荐用const常量代替)
ZBDeviceConst.h 系统相关宏定义
ZBStringConst.h 字符串相关宏定义
ZBDefineConst.h 自定义宏

1.系统相关宏定义
/** 1.设备状态条,导航栏,tabbar高度 */
#define ZB_HEIGHT_STATUSBAR     20.f // 状态条高度
#define ZB_HEIGHT_NAVIGATIONBAR 44.f // 导航栏高度
#define ZB_HEIGHT_TABBAR        49.f // tabbar高度

/** 2.设备屏幕宽度和高度(支持横屏) */
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // 当前Xcode支持iOS8及以上
#define ZB_SCREEN_WIDTH ([[UIScreen mainScreen] respondsToSelector:@selector(nativeBounds)]?[UIScreen mainScreen].nativeBounds.size.width/[UIScreen mainScreen].nativeScale:[UIScreen mainScreen].bounds.size.width)
#define ZB_SCREEN_HEIGHT ([[UIScreen mainScreen] respondsToSelector:@selector(nativeBounds)]?[UIScreen mainScreen].nativeBounds.size.height/[UIScreen mainScreen].nativeScale:[UIScreen mainScreen].bounds.size.height)
#define ZB_SCREEN_SIZE ([[UIScreen mainScreen] respondsToSelector:@selector(nativeBounds)]?CGSizeMake([UIScreen mainScreen].nativeBounds.size.width/[UIScreen mainScreen].nativeScale,[UIScreen mainScreen].nativeBounds.size.height/[UIScreen mainScreen].nativeScale):[UIScreen mainScreen].bounds.size)
#else
#define ZB_SCREEN_WIDTH     [UIScreen mainScreen].bounds.size.width
#define ZB_SCREEN_HEIGHT    [UIScreen mainScreen].bounds.size.height
#define ZB_SCREEN_SIZE      [UIScreen mainScreen].bounds.size
#endif

/**
 *  3.设备类型
 *  设备屏幕高度:IPHONE4(iPhone4,iPhone4s)480;IPHONE5(iPhone5,iPhone5s)568;IPHONE6 667;IPHONE6PLUS 736.
 *  EPSILON是最小误差,DBL_EPSILON是双浮点型(double)最小误差,是EPSILON+X不等于X的最小的正数
 */
#if TARGET_IPHONE_SIMULATOR // 模拟器
#define IPHONE4             ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )480 ) < DBL_EPSILON )
#define IPHONE5             ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#define IPHONE6             ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )667 ) < DBL_EPSILON )
#define IPHONE6PLUS         ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )736 ) < DBL_EPSILON )
#elif TARGET_OS_IPHONE // 真机
#define IPHONE4             ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )480 ) < DBL_EPSILON )
#define IPHONE5             ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#define IPHONE6             ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )667 ) < DBL_EPSILON )
#define IPHONE6PLUS         ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )736 ) < DBL_EPSILON )
#endif
// 判断是否为iPhone
#define ZB_IS_IPHONE           (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
// 判断是否为iPad
#define ZB_IS_IPAD             (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
// 判断是否为ipod
#define ZB_IS_IPOD             ([[[UIDevice currentDevice] model] isEqualToString:@"iPod touch"])


/**
 *  4.设备系统版本
 */
#define iOS6_LATER          ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0)
#define iOS7_LATER          ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
#define iOS8_LATER          ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
#define iOS9_LATER          ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0)
#define iOS7_0              ([[UIDevice currentDevice].systemVersion floatValue] == 7.0)
#define iOS7_1              ([[UIDevice currentDevice].systemVersion floatValue] == 7.1)
#define iOS8_0              ([[UIDevice currentDevice].systemVersion floatValue] == 8.0)
#define iOS8_1              ([[UIDevice currentDevice].systemVersion floatValue] == 8.1)
#define iOS8_2              ([[UIDevice currentDevice].systemVersion floatValue] == 8.2)
#define iOS8_3              ([[UIDevice currentDevice].systemVersion floatValue] == 8.3)
#define iOS9_0              ([[UIDevice currentDevice].systemVersion floatValue] == 9.0)
#define iOS9_1              ([[UIDevice currentDevice].systemVersion floatValue] == 7.1)

/** 5.获取temp,沙盒Document,沙盒Cache目录 */
#define ZB_PATH_TEMP        NSTemporaryDirectory()
#define ZB_PATH_DOCUMENT    [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
#define ZB_PATH_CACHE       [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]

/** 6.ARC/MRC */
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif

2.自定义宏

/** 1.颜色字体 */
#define ZB_COLOR_HEX(x)              ([UIColor colorWithHexColor:(x)])
#define ZB_COLOR(r,g,b)              ([UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:255/255.0])
#define ZB_COLOR_ALPHA(r,g,b,a)      ([UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)])
#define ZB_COLOR_RANDOM              ([UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0])
#define ZB_FONT(x)                   ([UIFont systemFontOfSize:(x)])

/** 2.空值判断 */
#define ZB_IS_EMPTY_STR(_str)        (((_str) == nil) || ([(_str) isEqual:[NSNull null]]) ||([(_str)isEqualToString:@""]))
#define ZB_IS_EMPTY_ARR(_arr)        (((_arr) == nil) || ([(_arr) isEqual:[NSNull null]]) ||([(_arr) count] == 0))

/** 3.单例 */
// 声明单例
#undef  ZB_SINGLETON_DEFINE
#define ZB_SINGLETON_DEFINE( __class ) \
+ (__class *)sharedInstance;
// 实现单例
#undef  ZB_SINGLETON_IMPLEMENT
#define ZB_SINGLETON_IMPLEMENT( __class ) \
+ (__class *)sharedInstance \
{ \
static __class * __singleton__ = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
__singleton__ = [[__class alloc] init]; \
}); \
return __singleton__; \
}

/** 4.打印日志 */
#ifdef DEBUG
#define ZBLog(...)  NSLog(__VA_ARGS__)
#define ZB_LOG_INT(N) NSLog(@"%d",(N))
#define ZB_LOG_FLOAT(F) NSLog(@"%f",(F))
#define ZB_LOG_INTEGER(I) NSLog(@"%ld",(long)(I))
#else
#define ZBLog(...)
#define ZB_LOG_INT(N)
#define ZB_LOG_FLOAT(F)
#define ZB_LOG_INTEGER(I)
#endif

/** 5.weakself/strongself */
#define ZB_WEAK_SELF(type)  __weak typeof(type) weak##type = type;
#define ZB_STRONG_SELF(type)  __strong typeof(weak##type) strong##type = weak##type;

/** 6.GCD */
// GCD - 一次性执行
#define ZB_DISPATCH_ONCE_BLOCK(onceBlock) static dispatch_once_t onceToken; dispatch_once(&onceToken, onceBlock);
// GCD - 在Main线程上运行
#define ZB_DISPATCH_MAIN_THREAD(mainQueueBlock) dispatch_async(dispatch_get_main_queue(), mainQueueBlock);
// GCD - 开启异步线程
#define ZB_DISPATCH_GLOBAL_QUEUE_DEFAULT(globalQueueBlock) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), globalQueueBlock);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值