iOS常见宏理解以及使用(更新ing)

7,507 阅读3分钟

FOUNDATION_EXPORT, UIKIT_EXTERN

该宏的作用类似于extern,使用方法也与extern类似,在.m文件中,定义如下

    NSString *const kFoundationExportString = @"Hello World";
    
    NSString *const kExternString = @"Hello World";

然后在.h文件中加上以下声明, 就可以在导入该.h文件的类中访问该常量。

    FOUNDATION_EXPORT NSString *const kFoundationExportString;
    
    extern NSString *const kExternString;  

如果要在未导入该.h文件的类中访问这两个常量, 则应该将上面的代码放入该类的.m文件中。 UIKIT_EXTERN相比extern只是增加了兼容性,使用方法一样。

使用如下:

    NSString *str = @"Hello World";
    if (str == kConstantString) {
        NSLog(@"equal");
    }

使用FOUNDATION_EXPORT声明的字符串常量比较的是指针的地址, 而#define宏定义的常量字符串只能使用isEqualToString来比较, 前者更加高效。

define与FOUNDATION_EXPORT比较
define与FOUNDATION_EXPORT比较

NS_STRING_ENUMNS_EXTENSIBLE_STRING_ENUM

这两个个宏定义是用于为Objective-C桥接Swift所使用的,它的作用是在桥接到 Swift 中时可进行枚举扩展,使用如下: 在.h文件中声明

    typedef NSString *ViewControllerKey NS_STRING_ENUM;
    FOUNDATION_EXPORT ViewControllerKey const ViewControllerKeyTitle;
    FOUNDATION_EXPORT ViewControllerKey const ViewControllerKeySubtitle;
    FOUNDATION_EXPORT ViewControllerKey const ViewControllerKeySummary;

.m文件中定义:

    ViewControllerKey const ViewControllerKeyTitle = @"title";
    ViewControllerKey const ViewControllerKeySubtitle = @"subtitle";
    ViewControllerKey const ViewControllerKeySummary = @"summary"; 

在swift文件中使用如下:

    print("\(ViewControllerKey.title) \(ViewControllerKey.subtitle) \(ViewControllerKey.summary)")

这两个宏定义的区别在于,NS_STRING_ENUM是确定的, NS_EXTENSIBLE_STRING_ENUM则是可扩展的,还可以在在Swift中进行扩展。

__VA_ARGS__

##就是个粘合剂,将前后两部分粘合起来,也就是有“字符化”的意思。

而__VA_ARGS__在预编译中会被实参列表取代, ...表示可变参列表。

##__VA_ARGS__ 宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的,去掉的作用,否则会编译出错

    #define BLOCK_EXEC(block, ...) if (block) { block(__VA_ARGS__); };

    #ifdef DEBUG
    #define LogInfo( s, ... ) NSLog( @"[LogInfo]<%@:(%d)> %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
    #else
    #define LogInfo( s, ... )
    #endif

weakifystrongify或者@weakify@strongify

这两组是宏功能都是一样的,解决block中的循环引用与增强引用防止被释放,前者是YYKit中的,后者则是SDwebImage中的。 我们先看一下YYKit中的实现:

#ifndef weakify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
        #endif
    #endif
#endif

#ifndef strongify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
        #endif
    #endif
#endif
  • __typeoftypeof是一样的,属于C的不同标准。
  • __has_feature: 判断传入值是不是同时支持Clang和当前语言的标准(ARC)。

Clang文档链接

Clang __has_feature
Clang __has_feature

实际预处理结果如下图:

weakify预处理结果
weakify preprocessed

SDWebImage的实现则是这样:

#ifndef weakify
    #define weakify(...) \
    sd_keywordify \
    metamacro_foreach_cxt(sd_weakify_,, __weak, __VA_ARGS__)
#endif

#ifndef strongify
    #define strongify(...) \
    sd_keywordify \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    metamacro_foreach(sd_strongify_,, __VA_ARGS__) \
    _Pragma("clang diagnostic pop")
#endif

sd_keywordify

#if DEBUG
    #define rac_keywordify autoreleasepool {}
#else
    #define rac_keywordify try {} @catch (...) {}
#endif

DEBUG模式下使用@autoreleasepool是为了维持编译器的分析能力,而使用@try@catch 是为了防止插入一些不必要的autoreleasepool

metamacro_foreach_cxt

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

metamacro_concat, metamacro_argcount

#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)
        
#define metamacro_concat_(A, B) A ## B

#define metamacro_argcount(...) \
        metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
        
        
#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__VA_ARGS__)
        

RAC中也有类似的宏定义

参考链接

源码地址:Github: ZpFate/DefineDemo