attribute是GNU C特色之一,在iOS用的比较广泛.系统中有许多地方使用到. attribute可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute)等.
函数属性(Function Attribute)
- noreturn
- noinline
- always_inline
- pure
- const
- nothrow
- sentinel
- format
- format_arg
- no_instrument_function
- section
- constructor
- destructor
- used
- unused
- deprecated
- weak
- malloc
- alias
- warn_unused_result
- nonnull
类型属性(Type Attributes)
- aligned
- packed
- transparent_union,
- unused,
- deprecated
- may_alias
变量属性(Variable Attribute)
- aligned
- packed
Clang特有的
- availability
- overloadable
书写格式
书写格式:attribute后面会紧跟一对原括弧,括弧里面是相应的attribute参数
__attribute__(xxx)
常见的系统用法
format
官方例子:NSLog
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
format属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。对于format参数的使用如下
format (archetype, string-index, first-to-check)
第一参数需要传递“archetype”指定是哪种风格,这里是 NSString;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定第一个可变参数所在的索引.
noreturn
官方例子: abort() 和 exit()
该属性通知编译器函数从不返回值。当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。
availability
官方例子:
-
- (CGSize)sizeWithFont:(UIFont *)font NS_DEPRECATED_IOS(2_0, 7_0, "Use -sizeWithAttributes:") __TVOS_PROHIBITED;
-
//来看一下 后边的宏
-
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
-
define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __attribute__((availability(ios,introduced=_iosIntro,deprecated=_iosDep,message="" __VA_ARGS__)))
-
//宏展开以后如下
-
__attribute__((availability(ios,introduced=2_0,deprecated=7_0,message=""__VA_ARGS__)));
-
//ios即是iOS平台
-
//introduced 从哪个版本开始使用
-
//deprecated 从哪个版本开始弃用
-
//message 警告的消息
availability属性是一个以逗号为分隔的参数列表,以平台的名称开始,包含一些放在附加信息里的一些里程碑式的声明。
-
introduced:第一次出现的版本。
-
deprecated:声明要废弃的版本,意味着用户要迁移为其他API
-
obsoleted: 声明移除的版本,意味着完全移除,再也不能使用它
-
unavailable:在这些平台不可用
-
message:一些关于废弃和移除的额外信息,clang发出警告的时候会提供这些信息,对用户使用替代的API非常有用。
-
这个属性支持的平台:ios,macosx。
简单例子:
-
//如果经常用,建议定义成类似系统的宏
-
- (void)oldMethod:(NSString *)string __attribute__((availability(ios,introduced=2_0,deprecated=7_0,message="用 -newMethod: 这个方法替代 "))){
-
NSLog(@"我是旧方法,不要调我");
-
}
-
- (void)newMethod:(NSString *)string{
-
NSLog(@"我是新方法");
-
}
效果:
Paste_Image.png
//如果调用了,会有警告
Paste_Image.png
unavailable
告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过init来初始化,只能通过特定的初始化方法()比如单例,就可以将init方法标记为unavailable;
-
//系统的宏,可以直接拿来用
-
#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))
-
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
-
@interface Person : NSObject
-
@property(nonatomic,copy) NSString *name;
-
@property(nonatomic,assign) NSUInteger age;
-
- (instancetype)init NS_UNAVAILABLE;
-
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
-
@end
Paste_Image.png
//实际上unavailable后面可以跟参数,显示一些信息,如:
-
//系统的
-
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
objc_root_class
表示这个类是一个根类(基类),比如NSObject,NSProxy.
-
//摘自系统
-
//NSProxy
-
NS_ROOT_CLASS
-
@interface NSProxy
{ -
Class isa;
-
}
-
//NSObject
-
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
-
OBJC_ROOT_CLASS
-
OBJC_EXPORT
-
@interface NSObject
{ -
Class isa OBJC_ISA_AVAILABILITY;
-
}
NSObject
@property (nonatomic,strong) __attribute__((NSObject)) CFDictionaryRef myDictionary;
CFDictionaryRef属于CoreFoundation框架的,也就是非OC对象,加上attribute((NSObject))后,myDictionary的内存管理会被当做OC对象来对待.
objc_designated_initializer
用来修饰类的designated initializer初始化方法,如果修饰的方法里没有调用super类的 designated initializer,编译器会发出警告。可以简写成NS_DESIGNATED_INITIALIZER
这篇文章讲的很好,建议参考这个.
https://yq.aliyun.com/articles/5847
visibility
语法:
__attribute__((visibility("visibility_type")))
其中,visibility_type 是下列值之一:
-
default
假定的符号可见性可通过其他选项进行更改。缺省可见性将覆盖此类更改。缺省可见性与外部链接对应。 -
hidden
该符号不存放在动态符号表中,因此,其他可执行文件或共享库都无法直接引用它。使用函数指针可进行间接引用。 -
internal
除非由 特定于处理器的应用二进制接口 (psABI) 指定,否则,内部可见性意味着不允许从另一模块调用该函数。 -
protected
该符号存放在动态符号表中,但定义模块内的引用将与局部符号绑定。也就是说,另一模块无法覆盖该符号。 -
除指定 default 可见性外,此属性都可与在这些情况下具有外部链接的声明结合使用。
您可在 C 和 C++ 中使用此属性。在 C++ 中,还可将它应用于类型、成员函数和命名空间声明。
系统用法:
-
// UIKIT_EXTERN extern
-
#ifdef __cplusplus
-
#define UIKIT_EXTERN extern "C" __attribute__((visibility ("default")))
-
#else
-
#define UIKIT_EXTERN extern __attribute__((visibility ("default")))
-
#endif
-
`
nonnull
编译器对函数参数进行NULL的检查,参数类型必须是指针类型(包括对象)
//使用
-
- (int)addNum1:(int *)num1 num2:(int *)num2 __attribute__((nonnull (1,2))){//1,2表示第一个和第二个参数不能为空
-
return *num1 + *num2;
-
}
-
- (NSString *)getHost:(NSURL *)url __attribute__((nonnull (1))){//第一个参数不能为空
-
return url.host;
-
}
常见用法
aligned
__attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐.例如:
不加修饰的情况
-
typedef struct
-
{
-
char member1;
-
int member2;
-
short member3;
-
}Family;
-
//输出字节:
-
NSLog(@"Family size is %zd",sizeof(Family));
-
//输出结果为:
-
2016-07-25 10:28:45.380 Study[917:436064] Family size is 12
//修改字节对齐为1
-
typedef struct
-
{
-
char member1;
-
int member2;
-
short member3;
-
}__attribute__ ((aligned (1))) Family;
-
//输出字节:
-
NSLog(@"Family size is %zd",sizeof(Family));
-
//输出结果为:
-
2016-07-25 10:28:05.315 Study[914:435764] Family size is 12
和上面的结果一致,因为 设定的字节对齐为1.而结构体中成员的最大字节数是int 4个字节,1 < 4,按照4字节对齐,和系统默认一致.
修改字节对齐为8
-
typedef struct
-
{
-
char member1;
-
int member2;
-
short member3;
-
}__attribute__ ((aligned (8))) Family;
-
//输出字节:
-
NSLog(@"Family size is %zd",sizeof(Family));
-
//输出结果为:
-
2016-07-25 10:28:05.315 Study[914:435764] Family size is 16
这里 8 > 4,按照8字节对齐,结果为16,不知道字节对齐的可以看我的这篇文章http://www.jianshu.com/p/f69652c7df99
可是想了半天,也不知道这玩意有什么用,设定值小于系统默认的,和没设定一样,设定大了,又浪费空间,效率也没提高,感觉学习学习就好.
packed
让指定的结构结构体按照一字节对齐,测试:
-
//不加packed修饰
-
typedef struct {
-
char version;
-
int16_t sid;
-
int32_t len;
-
int64_t time;
-
} Header;
-
//计算长度
-
NSLog(@"size is %zd",sizeof(Header));
-
输出结果为:
-
2016-07-22 11:53:47.728 Study[14378:5523450] size is 16
可以看出,默认系统是按照4字节对齐
-
//加packed修饰
-
typedef struct {
-
char version;
-
int16_t sid;
-
int32_t len;
-
int64_t time;
-
}__attribute__ ((packed)) Header;
-
//计算长度
-
NSLog(@"size is %zd",sizeof(Header));
-
输出结果为:
-
2016-07-22 11:57:46.970 Study[14382:5524502] size is 15
用packed修饰后,变为1字节对齐,这个常用于与协议有关的网络传输中.