iOS中的一些特殊语法

本篇文章来解释一些iOS代码中的特殊语法、写法,语法糖

1. FBKVOKeyPath

#define FBKVOKeyPath(KEYPATH) \
@(((void)(NO && ((void)KEYPATH, NO)), \
({ const char *fbkvokeypath = strchr(#KEYPATH, '.'); NSCAssert(fbkvokeypath, @"Provided key path is invalid."); fbkvokeypath + 1; })))

摘录自KVOController中FBKVOController.h
官方解释如下:

 This macro ensures that key path exists at compile time.
 Given a real receiver with a key path as you would call it, it verifies at compile time that the key path exists, without calling it.

 For example:

 FBKVOKeyPath(string.length) => @"length"

 Or even the complex case:

 FBKVOKeyPath(string.lowercaseString.length) => @"lowercaseString.length".

我们可以使用该宏定义获取string.property中的property属性,而且,默认转换成NSString类型

    1 校验传入的KeyPath是否有编译错误
    ((void)(NO && ((void)KEYPATH, NO))
    // NO && ... 是为了运行时直接返回NO减少操作, 因为有(void)KEYPATH的存在,所以编译时校验了object.property
    
    // 2 将传入的object.property转换为"property"
    { const char *fbkvokeypath = strchr(#KEYPATH, '.'); NSCAssert(fbkvokeypath, @"Provided key path is invalid."); fbkvokeypath + 1; }
    // 2.1 #KEYPATH将object.property转为字符串"object.property"
    // 2.2 strchr截取".property"
    // 2.3 NSCAssert保证fbkvokeypath有效
    // 2.4 ".property"+1="property"
    
    // 3 使用@()语法糖将char *转换为NSString类型
    @(((void)NO, "property"))
    // 因为','操作符是返回后面的值,即string = (@"a", @"b");string的值为@"b"

    2.1 附:c语言中,##表示把两个宏参数贴合在一起,而单个#的功能是将其后面的宏参数进行字符串化操作
    2.2 strchr()函数定义:
        char *strchr( const char *str, int ch ); 
        str: 传入的字符串指针
        ch:要搜索的字符
        return:返回特定字符**第一次**在字符串中的出现的指针,未找到时返回空指针
    2.3 NSCAssert和NSAssert区别,见2
    2.4 p+1 表示地址,指针p所指向的内存地址的下一个内存地址。

2. NSAssert和NSCAssert区别

在苹果的SDK中可以看到这两个都是定义的宏

#define NSAssert(condition, desc, ...)  \  
do {                \  
__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \  
if (!(condition)) {     \  
    [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \  
    object:self file:[NSString stringWithUTF8String:__FILE__] \  
        lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \  
}               \  
    __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \  
} while(0)  
#endif 
#define NSCAssert(condition, desc, ...) \  
do {                \  
__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \  
if (!(condition)) {     \  
    [[NSAssertionHandler currentHandler] handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \  
    file:[NSString stringWithUTF8String:__FILE__] \  
        lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \  
}               \  
    __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \  
} while(0)  
#endif 

都是使用断言来保证条件的正确性
但是要注意一点:
NSAssert在实现中,强引用了self当前对象,这里有可能会引起循环引用

3. iOS枚举中直接设置值为字母

一般情况,我们写枚举like this:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {  
    UIViewAnimationTransitionNone,//默认从0开始  
    UIViewAnimationTransitionFlipFromLeft,  
    UIViewAnimationTransitionFlipFromRight,  
    UIViewAnimationTransitionCurlUp,  
    UIViewAnimationTransitionCurlDown,  
};  
  
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {  
    UIViewAutoresizingNone                 = 0,  
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,  
    UIViewAutoresizingFlexibleWidth        = 1 << 1,  
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,  
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,  
    UIViewAutoresizingFlexibleHeight       = 1 << 4,  
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5  
}; 

但是,还会有这样的写法
比如:

typedef NS_ENUM(NSInteger, Test)
{
    TestA = 0,
    TestB = 1,
    TestC = 4,
    TestD = G,
};

这种情况,就是使用了"G"的ASCII,赋值给了TestD

参考资料1:KVOController详解
参考资料2:NSAssert,NSCassert
参考资料3:断言(NSAssert)的使用
参考资料4:iOS之枚举用法
参考资料5:Hello, 宏定义魔法世界

你可能感兴趣的:(iOS中的一些特殊语法)