RAC中的宏定义学习

RAC 的中有提示的宏定义的写法

// -  RACObserve的声明
#if __clang__ && (__clang_major__ >= 8)
#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH)
#else
#define RACObserve(TARGET, KEYPATH) \
({ \
	_Pragma("clang diagnostic push") \
	_Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
	_RACObserve(TARGET, KEYPATH) \
	_Pragma("clang diagnostic pop") \
})
#endif

// - _RACObserve的声明
#define _RACObserve(TARGET, KEYPATH) \
({ \
	__weak id target_ = (TARGET); \
	[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
})

// - keypath的声明
#define keypath(...) \
    metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))

#define keypath1(PATH) \
    (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))

#define keypath2(OBJ, PATH) \
    (((void)(NO && ((void)OBJ.PATH, NO)), # PATH))

使用(有代码提示)在这里插入图片描述

以上宏定义的解析

// - 主要牛逼的地方在@keypath(TARGET, KEYPATH)中 
[解析]
 @keypath(TARGET, KEYPATH)中的@() 可以将一个 C 语言字符串转成 OC 字符串
1. keypath1 解析  (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))
	1.void 是用来 强制类型转换的, 方式未使用时候的警告
	2. (NO && (PATH, NO)) 是一个表达式, 之所以会有代码提示, 就是因为这个表达式;
	3. # PATH 表示将 PATH 的内容转成字符串;
2. keypath1解析 (((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
	1.void 是用来 强制类型转换的, 方式未使用时候的警告
	2. (NO && (PATH, NO)) 是一个表达式, 之所以会有代码提示, 就是因为这个表达式;
	3. # PATH 表示将 PATH 的内容转成字符串;

以下是别人对这个宏定义的解析 :

在这里插入图片描述

自定义的这种宏定义

// - 一个参数和两个参数的宏定义
#define keypath1(PATH) (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))
#define keypath2(OBJ, PATH) @(((void)(NO && ((void)OBJ.PATH, NO)), # PATH))

宏定义使用方式二

// - 使用方式 1 限制传入参数个数
// - 定义 : 
#define COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, ...) RESULT
#define COUNT_PARMS(...) COUNT_PARMS2(__VA_ARGS__, 5, 4, 3, 2, 1)
#define C_ASSERT(test) \
    switch(0) {\
          case 0:\
          case test:;\
    }
    
// - 使用
C_ASSERT(COUNT_PARMS(1,2,3) == 2);   一下使用 , 以为会有两个 case 0, 所以会报错, (这种报错是在编译时期报错的!!!!)


// -  使用方式 2 (显示传入数字必须是枚举中的值)
// - 定义枚举
typedef enum : NSUInteger {
    MyEnumValueA = 1,
    MyEnumValueB = 2,
    MyEnumValueC = 3,
} MyEnum;

// - 定义宏
#define valueLimiter(x)    \
switch (0) {    \
    case 0: \
        break;  \
    case ((x >= MyEnumValueA && x <= MyEnumValueC)):    \
        break;  \
}

// - 使用宏定义
 valueLimiter(0); // - 报错
 valueLimiter(1);
 valueLimiter(2);
 valueLimiter(3);
 valueLimiter(4);// - 报错

限定传入参数数类型的宏定义

// - 限定类型的宏定义的书写
#define verifyCNum(num) autoreleasepool{}((void)(NO && (num * 0)))
#define verifyObj(obj) autoreleasepool{}((void)(NO && (object_getClassName(obj))))
#define verifyClass(class) autoreleasepool{}((void)(NO && ([class alloc])))

// - 宏定义的使用 
#define propertyGetter(class, instance, type, param) \
-(class *)instance##Mgr{ \
    @verifyCNum(type); \
    @verifyObj(param); \
    @verifyClass(class); \
    class <QIELiveRoomMgrProtocol> *instance = (class <QIELiveRoomMgrProtocol> *)[QIELiveRoomMgr mgrForType:type]; \
    if (!instance) { \
        if ([instance respondsToSelector:@selector(initWithParam:)]) { \
            instance = [[class alloc]initWithParam:param]; \
        }else{ \
            instance = [[class alloc]init]; \
        } \
        [QIELiveRoomMgr registerMgr:instance forType:type]; \
    } \
    return instance; \
}

// - 定义一个方法
@implementation DYNewPlayerViewController (Managers)
propertyGetter(QIEThemeRoomConfig, config, QIELiveRoomMgrTypeTheme, nil);
@end

限定方法必须实现或者声明

#ifndef Header_h
#define Header_h

#define shouldImplement1(obj, sel) (NO && ([obj sel], NO));
#define shouldImplement2(obj, sel) (NO && ([obj sel], NO), getSelName(@# sel));

/** selDic中如果有, 就直接返回, 如果没有就赋值 */
SEL getSelName(NSString *selName){
    
    // - 定义变量
    static NSMutableDictionary *selDic = NULL;
    __block NSString *funcName = [selDic objectForKey:selName];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        selDic = [NSMutableDictionary dictionary];
    });
    
    // - 已经缓存 直接返回
    if (funcName) return NSSelectorFromString(funcName);
    
    // - 未缓存
    funcName= @"";
    NSCharacterSet *separator = [NSCharacterSet characterSetWithCharactersInString:@": "];
    NSArray <NSString *> * strArray = [selName componentsSeparatedByCharactersInSet: separator];
    [strArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if(idx % 2 == 0) funcName = [funcName stringByAppendingFormat:@"%@:", obj];
    }];
    
    // - 缓存起来
    funcName = [funcName substringToIndex:funcName.length -1];
    [selDic setObject:funcName forKey:selName];
    
    // - 返回
    return NSSelectorFromString(funcName);
}
#endif /* Header_h */

// - 使用 
-(void)test:(SEL)selector{
    NSLog(@"-(void)test:(SEL)selector");
}

-(int)aa:(NSString *)xx aa:(int)aa rect:(CGRect)rect{
    NSLog(@"-(int)aa:(NSString *)xx aa:(int)aa rect:(CGRect)rect");
    return 1;
}

-(void)bb{
    NSLog(@"bb");
}
SEL s = shouldImplement2(self, aa:0 aa:0 rect:CGRectZero);
[self test:s];
shouldImplement2(self, bb);
shouldImplement2(self, aa:0 aa:0 rect:CGRectZero);
shouldImplement2(self, test:nil);

AFN 中使用宏定义调用 goto 语句 很妙

// - 定义宏
#define __Require_noErr_Quiet(errorCode, exceptionLabel)                      \
	  do                                                                          \
	  {                                                                           \
		  if ( __builtin_expect(0 != (errorCode), 0) )                            \
		  {                                                                       \
			  goto exceptionLabel;                                                \
		  }                                                                       \
	  } while ( 0 )

// - 在函数中使用
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
	// - 是用宏定义
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);

    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

_out:
    return isValid;
}

宏定义中使用 do{} while(0)的原因(吃掉分号)

************************* 方案一 ************************* 
// - 原始定义 : 
#define action(a,b)\
printf(a);\
printf(b);\

// - 使用 : 
if (x > y)
		action(x, y);
		
// - 宏定义展开为 此时代码不会同时执行
if (x > y)
printf(a);
printf(b);

************************* 方案二 ************************* 
// - 优化定义 : 
#define action(a,b)\
{printf(a)\
printf(b)}\

// - 使用 : 
if (x > y)
		action(x, y);
// - 宏定义展开为 看起来好像是没有问题
if (x > y){
	printf(a)
	printf(b)
}

// - 但是以下情况会报错
if (x > y)
		action(x, y);
else{
	printf(xx);
}

// - 宏定义展开为 此时多了个分号,编译不了
if (x > y){
	printf(a);
	printf(b);
};else{
	printf(xx);
}

************************* 方案三************************* 
// - 再次优化
#define action(a,b) do{printf(a); printf(b);}while(0)

// - 使用
if (x > y)
	action(x, y);
else{
	printf(xx);
}
// - 展开不会报错,可以正常使用
if (x > y)
	do{printf(a); printf(b);}while(0);
else{
	printf(xx);
}

rac 中使用attribute((cleanup(…)))

attribute((cleanup(…))) :作用域结束时自动执行一个指定方法
rac 中使用 block 使这个方法可以将本来要写在函数头和函数尾并且成对出现的代码写在一起

// - 定义宏
#define onExit \
    rac_keywordify \
    __strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^

// - 定义执行的函数
static inline void rac_executeCleanupBlock (__strong rac_cleanupBlock_t *block) {
    (*block)();
}

// - 使用 将本来要写在函数头和函数尾并且成对出现的代码写在一起
// -  (以为这里的函数传递的参数是指针类型, 很方便)受到这个启发,  可以应用于之前写的  [释放 strong 修饰的变量]  (https://blog.csdn.net/qq_27074387/article/details/99311843)
	[objectLock lock];
	@onExit {
			  [objectLock unlock];
			};

使用宏定义做版本判断

// - 以下语句用来做版本判断可以, 因为 development target 的 iOS 版本中有tintColor 函数, 但是当把development target 降低到 iOS 6.0 时候, 函数又找不到, 这样写就会报错,  所以就有用宏定义的写法

// - 普通写法
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {    
    self.window.tintColor = [UIColor redColor];    
}   

// - 使用宏定义的写法 
+(UIStatusBarStyle)defaultStyle{
#ifdef __IPHONE_13_0
    if (@available(iOS 13.0, *)) {
        return UIStatusBarStyleDarkContent;
    }else{
        return UIStatusBarStyleDefault;
    }
#endif
    return UIStatusBarStyleDefault;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章