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
两个宏也是
extern
只是为了兼容win32
同时适配C++编译器(其中__declspec(dllexport)
和__declspec(dllimport)
是C++定义的宏,可在 Mrcrosoft Developer 文档 查询)
用法如下:
用法跟 FOUNDATION_EXTERN 一样,不再举例。
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
表示iOS 上是
nonatomic
,适配了非iOS,其中__has_feature
表示当前新特性是否被Clang编译器支持和符合当前语言标准(可参考,其中__has_feature
的参数可在 PPMacroExpansion_8cpp_so 这查看可知,objc_property_explicit_atomic
参数表示Clang编译器是否支持atomic
关键字,因为OSX中不支持nonatomic
,因此OSX只有atomic
)
用法如下:
@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_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
表示标志子类继承这个方法时需要调用
super
,否则给出编译警告,其中objc_requires_super
可在 LLVM 的 AttributeReference 中查询,注意protocol
中无效
摘自文档:
- 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
}
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
指定类的初始化方法。指定初识化方法并不是对使用者。而是对内部的现实,其实就是必须调用父类的
designated initializer
方法,至于的作用,这里 有说明
摘自原文: 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 方法
实现父类的 init 方法,因为 init 也是NS_DESIGNATED_INITIALIZER 修饰
- (instancetype)init{
self.className = @"";
return self;
}
当然,此时毫无疑问会出现编译警告,原因很简单,本类中实现了 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;
}
此时编译警告就没了,也能正常使用,不过注意
initWithClassName
方法不要调用[self init]
,当然此时编译器很智能提示你,NS_DESIGNATED_INITIALIZER
修饰的方法只能使用super
调用其他NS_DESIGNATED_INITIALIZER
修饰的方法,如果你忽略这个警告,那么就会出现循环调用了,更多具体的说明可参考 Clang 拾遗之objc_designated_initializer
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
两个宏代表同一个东西,可以修饰任何东西(包括类、方法、属性、变量等等),表示当前编译器并不支持,或者说当前平台不支持或者无效,其中
__attribute__
上文也有介绍,是GCC
编译器特有的,用作描述详细信息,unavailable
语义上就是表示无效的,可以在 LLVM 的 AttributeReference 中查询。当然,苹果也解释了,只有某些编译器支持__attribute__((unavailable))
摘自原文: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)
...
其中很多涉及到都是
AVAILABLE
和DEPRECATED
,其实都表示当前API在不同的操作系统(这里只谈苹果的操作系统)的可用版本、废弃版本、淘汰版本,其中关键就是availability
,在 LLVM 的 AttributeReference 中有详细参数说明以及其用法举例,因此本文就不再啰嗦。还有一个visibility
,表示编译符号可见性,提供两个值default
和hidden
,这里有文章:控制符号的可见性 作了详细的说明,文中说了,visibility
可见性控制只能应用到代码中的C或者C++子集,不能应用到Objective-C的类和方法上。
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)
具体的定义在
CFAvaliability.h
,都是表示枚举,区别在于NS_ENUM
是通用枚举,而NS_OPTIONS
则是位移枚举,位移枚举的值就是用位移值表示,其中内部对编译器做了适配,__cplusplus
表示支持C++编译器,__has_feature(objc_fixed_enum)
表示当前编译器是否支持固定的没定义类型,参考 LLVM 的 LanguageExtensions
摘自原文:Use
__has_feature(objc_fixed_enum)
to determine whether support for fixed underlying types is available in Objective-C .
用法就不再举例,相信大家都用烂了~
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")
表示当前包在里面的属性都标记为
nonnull
,如果需要其他处理,则单独使用其他(例如:nullable
)标记,_Pragma("string")
与#pragma
字符串行为完全相同,在C/C++标准中,#pragma
是一条预处理的指令(preprocessor directive),在C++11中,标准定义了与预处理指令#pragma
功能相同的操作符_Pragma
。简单地说,#pragma
是用来向编译器传达语言标准以外的一些信息,例如我们平时经常使用的#pragma mark - <#name#>
,可参考 C/C++ 预处理器参考,那么上面的其实就是等同:
#pragma clang assume_nonnull begin
#pragma clang assume_nonnull end
用法如下:(Xcode10创建文件默认添加)
//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
最后
- 本文摘自这篇播客,感谢作者的努力!
参考资料:
- https://developer.apple.com
- http://clang.llvm.org/docs/AttributeReference.html
- https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Type-Attributes.html#Type-Attributes
- https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes
- https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html#Function-Attributes
- https://clang.llvm.org/docs/AutomaticReferenceCounting.html
- https://clang.llvm.org/docs/LanguageExtensions.html
- https://www.crest.iu.edu/projects/conceptcpp/docs/html-ext/AttrParsedAttrKinds_8inc_source.html
- https://gcc.gnu.org/onlinedocs/gcc-3.1.1/cpp/Common-Predefined-Macros.html#Common%20Predefined%20Macros
- http://docs.huihoo.com/doxygen/clang/r222231/PPMacroExpansion_8cpp_source.html
- http://gracelancy.com/blog/2014/05/05/variable-argument-lists/
- http://clang-developers.42468.n3.nabble.com/APPLE-CC-macro-td4046240.html#a4046243
- https://uranusjr.com/blog/post/53/objective-c-class-without-nsobject/
- http://blog.sunnyxx.com/2014/07/01/ios_ib_bridge/
- https://gist.github.com/steipete/9482253