attribute是GNU C特色之一,在iOS用的比较广泛.系统中有许多地方使用到. attribute可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute)等.
书写格式:attribute后面会紧跟一对原括弧,括弧里面是相应的attribute参数
__attribute__(xxx)
官方例子: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”指定第一个可变参数所在的索引.
官方例子: abort() 和 exit()
该属性通知编译器函数从不返回值。当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。
官方例子:
-
- (
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(
@"我是新方法");
-
}
效果:
//如果调用了,会有警告
告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过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
//实际上unavailable后面可以跟参数,显示一些信息,如:
-
//系统的
-
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
表示这个类是一个根类(基类),比如NSObject,NSProxy.
-
//摘自系统
-
//NSProxy
-
NS_ROOT_CLASS
-
@interface NSProxy <NSObject> {
-
Class isa;
-
}
-
-
//NSObject
-
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
-
OBJC_ROOT_CLASS
-
OBJC_EXPORT
-
@interface NSObject <NSObject> {
-
Class isa OBJC_ISA_AVAILABILITY;
-
}
@property (nonatomic,strong) __attribute__((NSObject)) CFDictionaryRef myDictionary;
CFDictionaryRef属于CoreFoundation框架的,也就是非OC对象,加上attribute((NSObject))后,myDictionary的内存管理会被当做OC对象来对待.
用来修饰类的designated initializer初始化方法,如果修饰的方法里没有调用super类的 designated initializer,编译器会发出警告。可以简写成NS_DESIGNATED_INITIALIZER
这篇文章讲的很好,建议参考这个.
https://yq.aliyun.com/articles/5847
语法:
__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
-
-
`
编译器对函数参数进行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;
-
}
__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修饰
-
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字节对齐,这个常用于与协议有关的网络传输中.
内联函数:内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理
内联的本质是用代码块直接替换掉函数调用处,好处是:快代码的执行,减少系统开销.适用场景:
使用例子:
-
//函数声明
-
void test(int a) __attribute__((always_inline));
当函数或者方法的返回值很重要时,要求调用者必须检查或者使用返回值,否则编译器会发出警告提示
-
- (BOOL)
availiable __attribute__((warn_unused_result))
-
{
-
return
10;
-
}
警告如下:
因为某些原因,我们不希望这个类被继承,也就是 "最终"的类,用法如下:
-
__attribute__((objc_subclassing_restricted))
-
@interface ViewController : UIViewController
-
-
-
@end
如果继承了这个类,编译器会报错
这个属性要求子类在重写父类的方法时,必须要重载父类方法,也就是调用super方法,否则警告.示例如下:
-
@interface ViewController : UIViewController
-
-
- (
void)jump __attribute__((objc_requires_super));
-
-
@end
-
-
- (
void)jump{
-
NSLog(
@"父类必须先执行");
-
}
-
-
-
@interface SGViewController : ViewController
-
-
@end
-
-
@implementation SGViewController
-
- (
void)jump{
-
NSLog(
@"子类才能再执行");
-
}
-
@end
警告如下:
实现类似于NSNumber 的快速打包能力@(...),一般对于struct,union我们只能通过NSValue将其打包. objc_boxable 可以帮助我们实现快速打包,示例如下:
-
//自定义结构体
-
typedef
struct __attribute__((objc_boxable)){
-
CGFloat x,y,width,height;
-
}SGRect;
-
-
SGRect rect = {
0,
0,
100,
200};
-
//这里直接打包成NSValue
-
NSValue *value = @(rect);
-
-
//这里我直接用系统的方法打印
-
NSLog(
@"%@",
NSStringFromCGRect(value.CGRectValue));
-
-
输出:
-
2016
-07
-21
21:
28:
43.538 Study[
14118:
5408921] {
{
0,
0}, {
100,
200}}
这样SGRect就具备快速打包功能了.
意思是: 构造器和析构器;constructor修饰的函数会在main函数之前执行,destructor修饰的函数会在程序exit前调用.
示例如下:
-
int main(
int argc,
char * argv[]) {
-
@autoreleasepool {
-
NSLog(
@"main");
-
return
UIApplicationMain(argc, argv,
nil,
NSStringFromClass([AppDelegate
class]));
-
}
-
}
-
-
__attribute__((constructor))
-
void before(){
-
NSLog(
@"before main");
-
}
-
-
__attribute__((destructor))
-
void after(){
-
NSLog(
@"after main");
-
}
-
-
//在viewController中调用exit
-
- (
void)viewDidLoad {
-
[
super viewDidLoad];
-
-
exit(
0);
-
}
-
输出如下:
-
-
2016
-07
-21
21:
49:
17.446 Study[
14162:
5415982] before main
-
2016
-07
-21
21:
49:
17.447 Study[
14162:
5415982] main
-
2016
-07
-21
21:
49:
17.534 Study[
14162:
5415982] after main
注意点:
实际上如果存在多个修饰过的函数,可以它们的调整优先级
代码如下:
-
int main(
int argc,
char * argv[]) {
-
@autoreleasepool {
-
NSLog(
@"main");
-
return
UIApplicationMain(argc, argv,
nil,
NSStringFromClass([AppDelegate
class]));
-
}
-
}
-
-
__attribute__((constructor(
101)))
-
void before1(){
-
NSLog(
@"before main - 1");
-
}
-
__attribute__((constructor(
102)))
-
void before2(){
-
NSLog(
@"before main - 2");
-
}
-
-
__attribute__((destructor(
201)))
-
void after1(){
-
NSLog(
@"after main - 1");
-
}
-
__attribute__((destructor(
202)))
-
void after2(){
-
NSLog(
@"after main - 2");
-
}
-
-
输出结果如下:
-
2016
-07
-21
21:
59:
35.622 Study[
14171:
5418393] before main -
1
-
2016
-07
-21
21:
59:
35.624 Study[
14171:
5418393] before main -
2
-
2016
-07
-21
21:
59:
35.624 Study[
14171:
5418393] main
-
2016
-07
-21
21:
59:
35.704 Study[
14171:
5418393] after main -
2
-
2016
-07
-21
21:
59:
35.704 Study[
14171:
5418393] after main -
1
注意点:
当函数声明和函数实现分开写时,格式如下:
-
static void before() __attribute__((
constructor));
-
-
static void before()
{
-
printf("before\n");
-
}
讨论:+load,constructor,main的执行顺序,代码如下:
-
+ (void)load
{
-
NSLog(@"load");
-
}
-
__attribute__((
constructor))
-
void
before
()
{
-
NSLog(@"before main");
-
}
-
-
输出结果如下:
-
2016-
07-
21
22:
13:
58.591 Study[
14185:
5421811] load
-
2016-
07-
21
22:
13:
58.592 Study[
14185:
5421811] before main
-
2016-
07-
21
22:
13:
58.592 Study[
14185:
5421811] main
可以看出执行顺序为:
load->constructor->main
为什么呢?
因为 dyld(动态链接器,程序的最初起点)在加载 image(可以理解成 Mach-O 文件)时会先通知 objc runtime 去加载其中所有的类,每加载一个类时,它的 +load 随之调用,全部加载完成后,dyld 才会调用这个 image 中所有的 constructor 方法,然后才调用main函数.
用来检查参数是否合法,只能用来修饰函数:
-
void printAge(int age)
-
__attribute__
((enable_if(age > 0 && age < 120, "你丫太监?")))
-
{
-
NSLog(@
"%d",age);
-
}
表示只能输入的参数只能是 0 ~ 120左右,否则编译报错
报错如下:
声明到一个变量上,当这个变量作用域结束时,调用指定的一个函数.如果不知道什么是作用域,请先学习一下.例子:
-
//这里传递的参数是变量的地址
-
void intCleanup(int *num){
-
NSLog(@
"cleanup------%d",*num);
-
}
-
-
- (
void)test{
-
int a __attribute__((cleanup(intCleanup))) =
10;
-
}
-
-
输出结果为:
-
2016-
07-
22
09:
59:
09.139 Study[
14293:
5495713] cleanup------
10
注意点:
示例代码:
-
void intCleanup(
int *num){
-
NSLog(
@"cleanup------%d",*num);
-
}
-
-
void stringCleanup(
NSString **str){
-
NSLog(
@"cleanup------%@",*str);
-
}
-
-
void rectCleanup(
CGRect *rect){
-
CGRect temp = *rect;
-
NSString *str =
NSStringFromCGRect(temp);
-
NSLog(
@"cleanup------%@",str);
-
}
-
-
-
int a __attribute__((cleanup(intCleanup))) =
10;
-
{
-
NSString *string __attribute__((cleanup(stringCleanup))) =
@"string";
-
CGRect rect __attribute__((cleanup(rectCleanup))) = {
0,
0,
1,
1};
-
}
-
-
-
输出结果为:
-
2016
-07
-22
10:
09:
36.621 Study[
14308:
5498861] cleanup------{
{
0,
0}, {
1,
1}}
-
2016
-07
-22
10:
09:
36.622 Study[
14308:
5498861] cleanup------string
-
2016
-07
-22
10:
09:
36.622 Study[
14308:
5498861] cleanup-----
-10
讨论:如果修饰了某个对象,那么cleanup和dealloc,谁先执行?
测试代码如下:
-
void objectCleanup(
NSObject **obj){
-
NSLog(
@"cleanup------%@",*obj);
-
}
-
-
- (
void)viewDidLoad {
-
[
super viewDidLoad];
-
ViewController *vc __attribute__((cleanup(objectCleanup))) = [[ViewController alloc] init];
-
}
-
-
- (
void)dealloc{
-
NSLog(
@"dealloc");
-
}
-
-
输出结果如下:
-
2016
-07
-22
10:
23:
08.839 Study[
14319:
5502769] cleanup------<ViewController:
0x13fe881e0>
-
2016
-07
-22
10:
23:
08.840 Study[
14319:
5502769] dealloc
可以明显看出,cleanup先于对象的dealloc执行.
-
//指向block的指针,觉得不好理解可以用typeof
-
void blockCleanUp(void(^*block)()){
-
(*block)();
-
}
-
-
void (^block)(
void) __attribute__((cleanup(blockCleanUp))) = ^{
-
NSLog(@
"finish block");
-
};
这个好处就是,不用等到block最后才写某些代码,我们可以把它放在block的任意位置,防止忘记.
用于c语言函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型:
-
__attribute__((overloadable)) void
print(NSString *
string){
-
NSLog(@
"%@",
string);
-
}
-
-
__attribute__((overloadable)) void
print(
int num){
-
NSLog(@
"%d",num);
-
}
-
-
//调用
-
print(
10);
-
print(@
"哈哈");
看到runtime是不是就感觉高大上,没错这个也跟运行时有关.作用是将将类或协议的名字在编译时指定成另一个.示例如下:
-
__attribute__((objc_runtime_name(
"NSObject")))
-
@interface SGObject :NSObject
-
-
@end
-
-
//调用
-
NSLog(
@"%@",[SGObject
class]);
-
//输出
-
2016
-07
-22
11:
18:
00.934 Study[
14355:
5516261]
NSObject
可以用来做代码混淆.
更多请看官网:
https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html
文章浏览阅读1.1w次,点赞11次,收藏6次。 今天使用Python图像处理库ImageGrab,在调用==grabclipboard()==方法获取到剪切板上图片的时候报了这个让我懵圈了的异常~~后来查了官方文档才知道,grabclipboard函数有一个缓存的问题,操作太快,有时候它就会读取上一次的内容,因为第一个没有读取到图像,所以报错了。..._attributeerror: 'nonetype' object has no attribute 'save
文章浏览阅读6.6k次,点赞22次,收藏56次。前两天在看TI官方提供的BasicRF的源码时,发现一个看不懂的地方,就是将一个数组名强制转换为结构体指针,如下所示。在上面的图片中,basicRfPktHdr_t是一个结构体,rxMpdu是一个长度为128个字节的数组名,pHdr是一个结构体指针。这让我很是清楚这么写是什么意思,因为以前从没有遇到,现在遇到了就算是进一步学习C语言了。通过百度查..._数组强制转换为结构体
文章浏览阅读492次。 接通电源之后,系统进行初始化,按下设置键S1或者S2,LCD进入时间设置界面,先使用齿轮确定要设定的位置(时、分、秒),按下s1按键进行确认,开始设置这个位置,可以使用齿轮电位计进行对该位置的数进行加减,设置数值,设置完成之后按下S1键进行确认,然后重新使用齿轮电位计确定要设置的位置,重复上述操作。全部设置完成之后,按下S2确认完成设置时间。接着进入倒计时器界面。计时完成之后,LED闪...___delay_cycles(1000000);
文章浏览阅读1.2w次,点赞4次,收藏8次。目前未找到删除文章的指令,可以到目录/source/_posts下删除相应的文章,然后重新生成部署即可到目录/source/_posts下删除相应的文章 $ hexo g $ hexo d_hexo 删除文章
文章浏览阅读8.7k次,点赞13次,收藏34次。刚安装了ubuntu系统,在此记录一下安装WPS过程。_ubuntu wps
文章浏览阅读1.4k次。共享资源,欢迎转载:http://hbhuanggang.cublog.cn 一、移植环境主 机:VMWare--Fedora 9 开发板:Mini2440--64MB Nand,Kernel:2.6.30.4 编译器:arm-linux-gcc-4.3.2.tgz u-boot:u-boot-2009.08.tar.bz2二、移植步骤上接:u-boo
文章浏览阅读2.9k次。不小心按快捷键按错了,结果出现这种圈圈叉叉,干扰布线解决方法:关掉这两个结果:解决!参考:参考的这个有点标题和内容不搭配。牛头马嘴,,,找不到component reference point和3D Body reference point..._ad原理图去除交叉点
文章浏览阅读3.3w次,点赞76次,收藏458次。小跳蚤 大用途前言: 算一算时间又快到了一年一度的毕业设计了吧,我也差不多完成我自己的毕业设计一年了,在此推出我的毕业设计成果以供后来的学弟学妹参考。都说站在巨人肩膀上,更上一层楼,在枯燥的编程期间我也有参考CSND大力哥的文章。很多人 把毕业设计应付过去,但是学习终究是自己的,绝知此事要躬行。接下来跟着我学习和分析的思路看一下成果吧!目录第1章 引言.... 5..._基于安卓的跳蚤市场论文
文章浏览阅读685次,点赞4次,收藏14次。文章目录一、什么是 Arthas二、特性一览三、Arthas 能为你做什么?四、快速安装1、前提条件2、一键安装五、快速使用1、启动并连接进程六、使用示例1、dashboard(当前系统的实时数据面板)2、sysprop(查看或修改java属性)3、mbean(实时查看Mbean信息)4、thread(查看线程)5、thread -n(查看占CPU前几的线程栈信息)6、jad(反编译代码)7、sc(查看已经加载的类)8、sm(列出某个类加载的方法)9、trace(跟踪方法的消耗时间)10、stack (查看_[root@localhost ~]# ./as.sh arthas script version: 3.7.2 [info] java_home: /
文章浏览阅读3.8k次。opencv 入门笔记十 图片序列保存为视频_opencv将图片保存为视频
文章浏览阅读1.1w次。caffe中实现SSD准确率评价方法是TestDetection()函数。mAp指标值是每个类别的Average precision的平均值。本文分析DetectionEvaluateLayer中实现评价的过程及其评价指标。需要指出的是,一般的前向过程是不包含DetectionEvaluateLayer层的定义的,只有在训练的评价(TEST)过程中才会使用到该layer。具体使用该层时,需要在..._目标检测overlap
文章浏览阅读1.1k次。这是我纯手写的《Go语言入门》,手把手教你入门Go。源码+文章,看了你就会????!文章中所有的代码我都放到了github.com/GanZhiXiong/go_learning这个仓库中!看文章时,对照仓库中代码学习效果更佳哦!目录什么是单向通道声明单向通道time包中的单向通道关闭通道只读通道不能关闭给关闭通道发送数据将会触发panic从已关闭的通道接收数据时将不会发生阻塞测试题测试1支持????什么是单向通道单向通道就是只能用于写入或读取数据的通道。它是对通道的一种使用限制。如果ch_go单向chan