搜索
您的当前位置:首页正文

NSObjCRuntime.h中你不知道的宏

来源:二三娱乐

前言

通过阅读别人的优秀源码,你会发现别人的开源API设计中,有一些宏你是经常忽略的,或者你不知道的。通过这些宏,可以让你的设计的API更加完善,当然看上去也会更加高端~举个栗子:

这些宏,你知道吗?
  • FOUNDATION_EXTERN

#if defined(__cplusplus)
#define FOUNDATION_EXTERN extern "C"
#else
#define FOUNDATION_EXTERN extern
#endif

表示 extern 全局变量,此时并没有分配内存,需要在.m文件中实现,此时为了支持C和C++混编(__cplusplus 是C++编译器内部定义的宏,在C++中,需要加
extern"C" 或包含在 extern "C" 块中),注意,此时外界是可以修改这个值,详细 extern 用法可自行查询相关资料,本文不详谈。

用法如下:

FOUNDATION_EXTERN NSString *name;// h文件
const NSString *name = @"gitKong";// m文件

  • FOUNDATION_EXPORT 、FOUNDATION_IMPORT

#if TARGET_OS_WIN32

    #if defined(NSBUILDINGFOUNDATION)
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllexport)
    #else
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllimport)
    #endif

    #define FOUNDATION_IMPORT FOUNDATION_EXTERN __declspec(dllimport)

#else
    #define FOUNDATION_EXPORT  FOUNDATION_EXTERN
    #define FOUNDATION_IMPORT FOUNDATION_EXTERN
#endif

用法如下:

用法跟 FOUNDATION_EXTERN 一样,不再举例。


  • NS_INLINE

#if !defined(NS_INLINE)
    #if defined(__GNUC__)
        #define NS_INLINE static __inline__ __attribute__((always_inline))
    #elif defined(__MWERKS__) || defined(__cplusplus)
        #define NS_INLINE static inline
    #elif defined(_MSC_VER)
        #define NS_INLINE static __inline
    #elif TARGET_OS_WIN32
        #define NS_INLINE static __inline__
    #endif
#endif

用法如下:

NS_INLINE int add(){
    // do something
}

  • FOUNDATION_STATIC_INLINE

#if !defined(FOUNDATION_STATIC_INLINE)
#define FOUNDATION_STATIC_INLINE static __inline__
#endif

表示static内联函数,可以看出,Xcode 的LLVM编译器(前身clang)使用
__inline__(Xcode 4之前是GCC)

用法跟 NS_INLINE 一样,不再举例。


  • FOUNDATION_EXTERN_INLINE

#if !defined(FOUNDATION_EXTERN_INLINE)
#define FOUNDATION_EXTERN_INLINE extern __inline__
#endif

表示全局内联函数

用法跟 NS_INLINE 一样,不再举例。


  • NS_REQUIRES_NIL_TERMINATION

#if !defined(NS_REQUIRES_NIL_TERMINATION)
    #if TARGET_OS_WIN32
        #define NS_REQUIRES_NIL_TERMINATION
    #else
        #if defined(__APPLE_CC__) && (__APPLE_CC__ >= 5549)
            #define NS_REQUIRES_NIL_TERMINATION __attribute__((sentinel(0,1)))
        #else
            #define NS_REQUIRES_NIL_TERMINATION __attribute__((sentinel))
        #endif
    #endif
#endif

截图如下:


Paste_Image.png

用法如下: 注意,如果使用C函数,NS_REQUIRES_NIL_TERMINATION 需要修饰声明,修饰函数定义会报警告

// __attribute__((sentinel)) 用于声明,用于实现会警告,此时就提示最后一个参数需要添加NULL
void mutableParams(NSString *first, ...) NS_REQUIRES_NIL_TERMINATION;
void mutableParams(NSString *first, ...){
    va_list args;
    va_start(args, first);
    NSString *next;
    while ((next = va_arg(args, NSString *))) {
        NSLog(@"%@",next);
    }
    va_end(args);
}
缺少NULL报警告
  • NS_BLOCKS_AVAILABLE

#if !defined(NS_BLOCKS_AVAILABLE)
    #if __BLOCKS__ && (MAC_OS_X_VERSION_10_6 <= MAC_OS_X_VERSION_MAX_ALLOWED || __IPHONE_4_0 <= __IPHONE_OS_VERSION_MAX_ALLOWED)
        #define NS_BLOCKS_AVAILABLE 1
    #else
        #define NS_BLOCKS_AVAILABLE 0
    #endif
#endif

表示当前的 block 是否有效,iOS 4.0之后都适用的(因为block 是 iOS 4.0 之后才有的)

用法如下:

#if NS_BLOCKS_AVAILABLE
    
        
    }
#else
    // do something
#endif

  • NS_NONATOMIC_IOSONLY

// Marks APIs whose iOS versions are nonatomic, that is cannot be set/get from multiple threads safely without additional synchronization
#if !defined(NS_NONATOMIC_IOSONLY)
    #if TARGET_OS_IPHONE
    #define NS_NONATOMIC_IOSONLY nonatomic
    #else
        #if __has_feature(objc_property_explicit_atomic)
            #define NS_NONATOMIC_IOSONLY atomic
        #else
            #define NS_NONATOMIC_IOSONLY
        #endif
    #endif
#endif

用法如下:

@property (NS_NONATOMIC_IOSONLY,copy) NSString *className;

  • NS_NONATOMIC_IPHONEONLY

// Use NS_NONATOMIC_IOSONLY instead of this older macro
#if !defined(NS_NONATOMIC_IPHONEONLY)
#define NS_NONATOMIC_IPHONEONLY NS_NONATOMIC_IOSONLY
#endif

历史原因,等同 NS_NONATOMIC_IOSONLY,苹果建议使用NS_NONATOMIC_IOSONLY

用法和NS_NONATOMIC_IOSONLY 一样


  • NS_FORMAT_FUNCTION(F,A)

// Marks APIs which format strings by taking a format string and optional varargs as arguments
#if !defined(NS_FORMAT_FUNCTION)
    #if (__GNUC__*10+__GNUC_MINOR__ >= 42) && (TARGET_OS_MAC || TARGET_OS_EMBEDDED)
    #define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
    #else
    #define NS_FORMAT_FUNCTION(F,A)
    #endif
#endif
  • 表示是 Format string 方案来实现不定参数(不同于哨兵方案NS_REQUIRES_NIL_TERMINATION(va_list)) GCC 3.2.1 will define __GNUC__ to 3, __GNUC_MINOR__ to 2
  • __attribute__((format)) 标示一个可变参函数使用了 Format string ,从而在编译时对其进行检查,其定义为 format (archetype, string-index, first-to-check),其中archetype代表 format string 的类型,它可以是 printfscanfstrftime或者strfmonCocoa开发者还可以使用__NSString__来指定其使用和 [NSString stringWithFormat:]NSLog() 一致的 Format string 规则。string-index 代表 Format string是第几个参数,first-to-check 则代表了可变参数列表从第几个参数开始
  • F 就是 Format string 是第F个参数,A 就是代表可变参数列表是从第A个参数开始

用法如下:

- (void)append:(NSString *)format,...NS_FORMAT_FUNCTION(1,2){
    // do something
}
[self append:@"%@%@",@"hello",@"gitKong"];

如何使用Format string 方式实现系统的 stringWithFormat ,而使用 Sentinel value (哨兵)方式,很简单:

- (NSString *)append:(NSString *)format,...NS_FORMAT_FUNCTION(1,2){
    va_list ap;
    va_start(ap, format);
    NSString *information = [[NSString alloc] initWithFormat:format arguments:ap];
    va_end(ap);
    return information;
}

  • NS_FORMAT_ARGUMENT(A)

// Marks APIs which are often used to process (take and return) format strings, so they can be used in place of a constant format string parameter in APIs
#if !defined(NS_FORMAT_ARGUMENT)
    #if defined(__clang__)
    #define NS_FORMAT_ARGUMENT(A) __attribute__ ((format_arg(A)))
    #else
    #define NS_FORMAT_ARGUMENT(A)
    #endif
#endif
  • 使用此宏后修饰的API必须有返回值,而且返回值必须是 String type
  • 如果修饰的是C函数,最好在函数声明中修饰。
  • 如果参数被约束,那么参数必须是一个 String type

用法如下:

NSString * testArgument(NSString *name,NSString *age,NSString *sex) NS_FORMAT_ARGUMENT(3);
NSString * testArgument(NSString *name,NSString *age,NSString *sex){
    return @"gitKong";
}

- (NSString *)testArg:(NSString *)name age:(NSString *)age NS_FORMAT_ARGUMENT(1){
    return [NSString stringWithFormat:@"hello %@,i am %@ years old",name,age];
}
修饰C函数实现报警告 参数不是String类型 只约束了第一个参数,因此第二个参数不受限制 必须要返回`String type`
  • __has_attribute、__has_extension、__has_feature

// Some compilers provide the capability to test if certain features are available. This macro provides a compatibility path for other compilers.
#ifndef __has_feature
#define __has_feature(x) 0
#endif

#ifndef __has_extension
#define __has_extension(x) 0
#endif

// Some compilers provide the capability to test if certain attributes are available. This macro provides a compatibility path for other compilers.
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif

摘自文档(针对 __attribute__):This function-like macro takes a single identifier argument that is the name of a GNU-style attribute. It evaluates to 1 if the attribute is supported by the current compilation target, or 0 if not.

  • 其中 __has_attribute(x)__has_feature(x) 为 0,表示 Compatibility with non-clang compilers,意思就是没有Clang编码
  • __has_extension 为 0,表示 Compatibility with pre-3.0 compilers,意思就是当前的feature 在当前语音中不被Clang 编译器支持

用法如下:


  • NS_RETURNS_RETAINED、NS_RETURNS_NOT_RETAINED

// Marks methods and functions which return an object that needs to be released by the caller but whose names are not consistent with Cocoa naming rules. The recommended fix to this is to rename the methods or functions, but this macro can be used to let the clang static analyzer know of any exceptions that cannot be fixed.
// This macro is ONLY to be used in exceptional circumstances, not to annotate functions which conform to the Cocoa naming rules.
#if __has_feature(attribute_ns_returns_retained)
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#else
#define NS_RETURNS_RETAINED
#endif
// Marks methods and functions which return an object that may need to be retained by the caller but whose names are not consistent with Cocoa naming rules. The recommended fix to this is to rename the methods or functions, but this macro can be used to let the clang static analyzer know of any exceptions that cannot be fixed.
// This macro is ONLY to be used in exceptional circumstances, not to annotate functions which conform to the Cocoa naming rules.
#if __has_feature(attribute_ns_returns_not_retained)
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
#else
#define NS_RETURNS_NOT_RETAINED
#endif
@interface Sark : NSObject
+ (instancetype)sarkWithMark:(NSString *)mark NS_RETURNS_NOT_RETAINED; // 1
- (instancetype)initWithMark:(NSString *)mark NS_RETURNS_RETAINED; // 2
@end

  • NS_RETURNS_INNER_POINTER

#ifndef NS_RETURNS_INNER_POINTER
#if __has_attribute(objc_returns_inner_pointer)
#define NS_RETURNS_INNER_POINTER __attribute__((objc_returns_inner_pointer))
#else
#define NS_RETURNS_INNER_POINTER
#endif
#endif

摘自文档:An Objective-C method returning a non-retainable pointer may be annotated with the objc_returns_inner_pointer attribute to indicate that it returns a handle to the internal data of an object, and that this reference will be invalidated if the object is destroyed. When such a message is sent to an object, the object’s lifetime will be extended until at least the earliest of:

  • the last use of the returned pointer, or any pointer derived from it, in the calling function or
  • the autorelease pool is restored to a previous state.

用法如下:(摘自系统API)

@property (nullable, readonly) const char *UTF8String NS_RETURNS_INNER_POINTER; // Convenience to return null-terminated UTF8 representation

  • NS_AUTOMATED_REFCOUNT_UNAVAILABLE

// Marks methods and functions which cannot be used when compiling in automatic reference counting mode.
#if __has_feature(objc_arc)
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
#else
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
#endif

用法如下:

void c_method_test() NS_AUTOMATED_REFCOUNT_UNAVAILABLE{
    
}

- (void)oc_method_test NS_AUTOMATED_REFCOUNT_UNAVAILABLE{
    
}
无法在ARC中使用
  • NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE

// Marks classes which cannot participate in the ARC weak reference feature.
#if __has_attribute(objc_arc_weak_reference_unavailable)
#define NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE __attribute__((objc_arc_weak_reference_unavailable))
#else
#define NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE
#endif

用法如下:

NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE @interface MacrosIntroduction : NSObject

@end
弱引用就报错
  • NS_REQUIRES_PROPERTY_DEFINITIONS

// Marks classes that must specify @dynamic or @synthesize for properties in their @implementation (property getters & setters will not be synthesized unless the @synthesize directive is used)
#if __has_attribute(objc_requires_property_definitions)
#define NS_REQUIRES_PROPERTY_DEFINITIONS __attribute__((objc_requires_property_definitions)) 
#else
#define NS_REQUIRES_PROPERTY_DEFINITIONS
#endif

用法如下:

NS_REQUIRES_PROPERTY_DEFINITIONS @interface MacrosIntroduction : NSObject

@property (NS_NONATOMIC_IPHONEONLY,copy) NSString *className;

@end
@implementation MacrosIntroduction

@synthesize className;

@end
没有实现 `@dynamic` or `@synthesize`
  • NS_REPLACES_RECEIVER

// Decorates methods in which the receiver may be replaced with the result of the method. 
#if __has_feature(attribute_ns_consumes_self)
#define NS_REPLACES_RECEIVER __attribute__((ns_consumes_self)) NS_RETURNS_RETAINED
#else
#define NS_REPLACES_RECEIVER
#endif

用法如下:

- (instancetype)hello NS_REPLACES_RECEIVER;// 此时方法命名不规范,只是作演示效果
- (instancetype)hello{
    return self = [super init];
}
没有使用 `NS_REPLACES_RECEIVER` 修饰

系统例子:NSObject (NSCoderMethods)

- (nullable id)awakeAfterUsingCoder:(NSCoder *)aDecoder NS_REPLACES_RECEIVER;

  • NS_RELEASES_ARGUMENT

#if __has_feature(attribute_ns_consumed)
#define NS_RELEASES_ARGUMENT __attribute__((ns_consumed))
#else
#define NS_RELEASES_ARGUMENT
#endif

摘自文档:The 'ns_consumed' attribute can be placed on a specific parameter in either the declaration of a function or an Objective-C method. It indicates to the static analyzer that a release message is implicitly sent to the parameter upon completion of the call to the given function or method. The Foundation framework defines a macro NS_RELEASES_ARGUMENT that is functionally equivalent to the NS_CONSUMED macro shown below.

用法如下:

- (void)oc_method_test1:(NSString *)name age:(NS_RELEASES_ARGUMENT NSString *)age{
    NSLog(@"age = %@",age);
}

  • NS_VALID_UNTIL_END_OF_SCOPE

// Mark local variables of type 'id' or pointer-to-ObjC-object-type so that values stored into that local variable are not aggressively released by the compiler during optimization, but are held until either the variable is assigned to again, or the end of the scope (such as a compound statement, or method definition) of the local variable.
#ifndef NS_VALID_UNTIL_END_OF_SCOPE
#if __has_attribute(objc_precise_lifetime)
#define NS_VALID_UNTIL_END_OF_SCOPE __attribute__((objc_precise_lifetime))
#else
#define NS_VALID_UNTIL_END_OF_SCOPE
#endif
#endif
  • 至于具体的应用场景也说不出,反正遇到了就知道有这个东西可以防止提前释放。

用法如下:

- (void)oc_method_test {
    NS_VALID_UNTIL_END_OF_SCOPE id className = @"xxx";
}

  • NS_ROOT_CLASS

// Annotate classes which are root classes as really being root classes
#ifndef NS_ROOT_CLASS
#if __has_attribute(objc_root_class)
#define NS_ROOT_CLASS __attribute__((objc_root_class))
#else
#define NS_ROOT_CLASS
#endif
#endif

表示当前修饰的类就是根类

用法如下:(NSObject 类就是 OBJC_ROOT_CLASSNS_ROOT_CLASS 一样,都是 __attribute__((objc_root_class))

NS_ROOT_CLASS
@interface MacrosIntroduction

@end

此时可以实现类似 NSObjectNSProxy 一样,注意,此时去掉 NS_ROOT_CLASS 就会编译失败

编译失败,需要添加父类

拓展:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-root-class"

同样可以避免编译失败,但此时的作用是,忽略编译器对根类的警告,意味着这样做不安全,但是 __attribute__((objc_root_class)) 在以前的 GCC Objective-C extension 编译器是无法识别

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-root-class"
@interface MacrosOtherClass

@end

  • NS_REQUIRES_SUPER

#ifndef NS_REQUIRES_SUPER
#if __has_attribute(objc_requires_super)
#define NS_REQUIRES_SUPER __attribute__((objc_requires_super))
#else
#define NS_REQUIRES_SUPER
#endif
#endif

摘自文档:

  • Some Objective-C classes allow a subclass to override a particular method in a parent class but expect that the overriding method also calls the overridden method in the parent class. For these cases, we provide an attribute to designate that a method requires a “call to super” in the overriding method in the subclass.
  • This attribute can only be applied the method declarations within a class, and not a protocol. Currently this attribute does not enforce any placement of where the call occurs in the overriding method (such as in the case of -dealloc where the call must appear at the end). It checks only that it exists.

用法如下:

- (void)oc_method_mustCallSuper NS_REQUIRES_SUPER;// 父类中声明
- (void)oc_method_mustCallSuper{
    [super oc_method_mustCallSuper];// 子类中实现需要调用super
}
不调用super,编译器警告 protocol 中无效
  • NS_DESIGNATED_INITIALIZER

#ifndef NS_DESIGNATED_INITIALIZER
#if __has_attribute(objc_designated_initializer)
#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
#else
#define NS_DESIGNATED_INITIALIZER
#endif
#endif

摘自原文:
To clarify the distinction between designated and secondary initializers clear, you can add the NS_DESIGNATED_INITIALIZER macro to any method in the init family, denoting it a designated initializer. Using this macro introduces a few restrictions:

  • The implementation of a designated initializer must chain to a superclass init method (with [super init...]) that is a designated initializer for the superclass.
  • The implementation of a secondary initializer (an initializer not marked as a designated initializer within a class that has at least one initializer marked as a designated initializer) must delegate to another initializer (with [self init...]).
  • If a class provides one or more designated initializers, it must implement all of the designated initializers of its superclass.

用法如下:

- (instancetype)initWithClassName:(NSString *)name NS_DESIGNATED_INITIALIZER;// 方法声明

- (instancetype)initWithClassName:(NSString *)name{// 方法实现
    if (self = [super init]) {
        self.className = name;
    }
    return self;
}

此时编译器就会出现警告,原因很简单,本类中实现了 NS_DESIGNATED_INITIALIZER 那么必须实现父类的 NS_DESIGNATED_INITIALIZER 方法

必须实现父类的 `NS_DESIGNATED_INITIALIZER` 方法

实现父类的 init 方法,因为 init 也是NS_DESIGNATED_INITIALIZER 修饰

- (instancetype)init{
    self.className = @"";
    return self;
}

当然,此时毫无疑问会出现编译警告,原因很简单,本类中实现了 NS_DESIGNATED_INITIALIZER ,那么构造方法中必须调用NS_DESIGNATED_INITIALIZER 方法,因此这里就有个大坑,操作不慎容易造成循环调用,下面会解释

编译警告:没调用`NS_DESIGNATED_INITIALIZER` 方法

实现父类的 init 方法并调用NS_DESIGNATED_INITIALIZER 修饰的方法

- (instancetype)init{
    return [self initWithClassName:@"gitKong"];
}

- (instancetype)initWithClassName:(NSString *)name{
    if (self = [super init]) {
        self.className = name;
    }
    return self;
}
编译警告:必须使用super
  • NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION

#ifndef NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION
#if __has_attribute(objc_protocol_requires_explicit_implementation)
#define NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION __attribute__((objc_protocol_requires_explicit_implementation))
#else
#define NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION
#endif
#endif

摘自原文:

  • Per more discussion, 'objc_protocol_requires_explicit_implementation' is
    refinement that it mainly adds that requirement that a protocol must be
    explicitly satisfied at the moment the first class in the class hierarchy
    conforms to it. Any subclasses that also conform to that protocol,
    either directly or via conforming to a protocol that inherits that protocol,
    do not need to re-implement that protocol.
  • Doing this requires first doing a pass on the super class hierarchy,
    gathering the set of protocols conformed to by the super classes,
    and then culling those out when determining conformance. This
    two-pass algorithm could be generalized for all protocol checking,
    and could possibly be a performance win in some cases. For now
    we restrict this change to protocols with this attribute to isolate
    the change in logic (especially as the design continues to evolve).

用法如下:

NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION
@protocol MacrosIntroductionProtocol <NSObject>
@optional
- (void)sayHello;

@required
- (void)sayHi;

@end
NS_REQUIRES_PROPERTY_DEFINITIONS @interface MacrosIntroduction:NSObject<MacrosIntroductionProtocol>// 准守协议
编译警告:`@required` 修饰的协议方法没有实现
  • NS_NO_TAIL_CALL

#if __has_attribute(not_tail_called)
#define NS_NO_TAIL_CALL __attribute__((not_tail_called))
#else
#define NS_NO_TAIL_CALL
#endif

摘自原文:The not_tail_called attribute prevents tail-call optimization on statically bound calls. It has no effect on indirect calls. Virtual functions, objective-c methods, and functions marked as always_inline cannot be marked as not_tail_called.

用法如下:

void NS_NO_TAIL_CALL print(){
    printf("---\n");
}

系统的 NSLog 其实也是用 NS_NO_TAIL_CALL 修饰,至于具体用法和效果,笔者也没找到相关详细介绍

FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;

什么是间接调用,其实就是用变量存储起来,OC里面用闭包,因为函数不是一等公民,swift的话就是一等公民了

void hi(){
    void (*func)() = &print;
    (*func)();// === func() === print()
}

  • NS_UNAVAILABLE、UNAVAILABLE_ATTRIBUTE

#if !defined(NS_UNAVAILABLE)
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
#endif
/*
 * only certain compilers support __attribute__((unavailable))
 */
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
    #define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))
#else
    #define UNAVAILABLE_ATTRIBUTE
#endif

摘自原文:This declaration is never available on this platform.

用法如下:

- (void)sayHi NS_UNAVAILABLE;
此时使用就出现编译错误
  • NS_AVAILABLE、NS_DEPRECATED 等

#include <CoreFoundation/CFAvailability.h>

#define NS_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define NS_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)

#define NS_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

#define NS_DEPRECATED_WITH_REPLACEMENT_MAC(_rep, _macIntroduced, _macDeprecated) API_DEPRECATED_WITH_REPLACEMENT(_rep, macosx(_macIntroduced, _macDeprecated)) API_UNAVAILABLE(ios, watchos, tvos)

#define NS_ENUM_AVAILABLE(_mac, _ios) CF_ENUM_AVAILABLE(_mac, _ios)
#define NS_ENUM_AVAILABLE_MAC(_mac) CF_ENUM_AVAILABLE_MAC(_mac)
#define NS_ENUM_AVAILABLE_IOS(_ios) CF_ENUM_AVAILABLE_IOS(_ios)

#define NS_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

#define NS_AVAILABLE_IPHONE(_ios) CF_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED_IPHONE(_iosIntro, _iosDep) CF_DEPRECATED_IOS(_iosIntro, _iosDep)

...

  • NS_ENUM、NS_OPTIONS

/* NS_ENUM supports the use of one or two arguments. The first argument is always the integer type used for the values of the enum. The second argument is an optional type name for the macro. When specifying a type name, you must precede the macro with 'typedef' like so:
 
typedef NS_ENUM(NSInteger, NSComparisonResult) {
    ...
};
 
If you do not specify a type name, do not use 'typedef'. For example:
 
NS_ENUM(NSInteger) {
    ...
};
*/
#define NS_ENUM(...) CF_ENUM(__VA_ARGS__)
#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)

摘自原文:Use __has_feature(objc_fixed_enum) to determine whether support for fixed underlying types is available in Objective-C.

用法就不再举例,相信大家都用烂了~


  • CF_STRING_ENUM、CF_EXTENSIBLE_STRING_ENUM

#ifndef CF_STRING_ENUM
#if __has_attribute(swift_wrapper)
#define _CF_TYPED_ENUM __attribute__((swift_wrapper(enum)))
#else
#define _CF_TYPED_ENUM
#endif

#define CF_STRING_ENUM _CF_TYPED_ENUM
#endif

#ifndef CF_EXTENSIBLE_STRING_ENUM
#if __has_attribute(swift_wrapper)
#define _CF_TYPED_EXTENSIBLE_ENUM __attribute__((swift_wrapper(struct)))
#else
#define _CF_TYPED_EXTENSIBLE_ENUM
#endif

#define CF_EXTENSIBLE_STRING_ENUM _CF_TYPED_EXTENSIBLE_ENUM
#endif
/* */

#define _NS_TYPED_ENUM _CF_TYPED_ENUM
#define _NS_TYPED_EXTENSIBLE_ENUM _CF_TYPED_EXTENSIBLE_ENUM

#define NS_STRING_ENUM _NS_TYPED_ENUM
#define NS_EXTENSIBLE_STRING_ENUM _NS_TYPED_EXTENSIBLE_ENUM
Not Found

用法如下:

typedef CF_STRING_ENUM NS_ENUM(NSInteger ,FLSystemEnum){
    FLSystemEnumOne,
    FLSystemEnumTwo = 1
} ;

  • NS_ASSUME_NONNULL_BEGIN、NS_ASSUME_NONNULL_END

#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define NS_ASSUME_NONNULL_END   _Pragma("clang assume_nonnull end")
#pragma clang assume_nonnull begin
#pragma clang assume_nonnull end

用法如下:

//NS_ASSUME_NONNULL_BEGIN
#pragma clang assume_nonnull begin
@interface MacrosIntroduction:NSObject
@property (nonatomic,assign) FLSystemEnum systemEnums;
@property (nullable, nonatomic,copy) NSString *xxx;
@end
#pragma clang assume_nonnull end
//NS_ASSUME_NONNULL_END

  • NS_SWIFT_NAME

#define NS_SWIFT_NAME(_name) CF_SWIFT_NAME(_name)

摘自原文:
C APIs, such as the Core Foundation framework, often provide functions that create, access, or modify C structures. You can use the CF_SWIFT_NAME macro in your own code to have Swift import C functions as members of the imported structure type.

用法官方文档有详细例子,此处不再举例


还有一些针对 swift 的宏,例如 NS_SWIFT_UNAVAILABLE(表示在swift中无效)、NS_NOESCAPEswift中有逃逸概念,默认闭包是noescape)、NS_SWIFT_NOTHROW(意思就是在swift中没有错误抛出) ,从语义上就能看什么作用,这里就不再做详细分析。

NSObjCRuntime.h 最后还有一下基本运算的宏,例如大家熟悉的 YES or NO定义、Min or MAXABS


  • 最后

    • 花了不少时间,资料总算整理好了,通过整理这份资料,也了解了不少编译器方面的知识,希望能帮到大家。

    • 如果文中有不对的地方,或者有什么建议,请务必提出喔,喜欢我的文章,可以点个like,加个关注,我会不定时更新文章。


参考资料:

Top