小谈编译器属性`__attribute__`

编译器属性 __attribute__ 用于向编译器描述特殊的标识、检查或优化。

__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。

修饰函数

constructor

使用 constructor 属性修饰函数,使该函数能够在 main() 函数之前执行。

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
__attribute__((constructor)) void testConstructor()
{
NSLog(@"使用‘constructor’修饰函数,将执行在 ‘main’ 函数之前");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
}
return 0;
}

运行结果:

1
2
3
2017-04-21 20:14:02.640374+0800 Test__attribute__[1575:31682] 使用‘constructor’修饰函数,将执行在 ‘main’ 函数之前
2017-04-21 20:14:02.640589+0800 Test__attribute__[1575:31682] 'Main' 函数开始执行
Program ended with exit code: 0

destructor

使用 destructor 属性修饰函数,使该函数能够在 main() 函数结束后或者 exit() 函数调用后执行。

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
__attribute__((destructor)) void testDestructor()
{
NSLog(@"使用‘destructor’修饰函数,将执行在 ‘main’ 函数之后");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
}
return 0;
}

运行结果:

1
2
3
2017-04-21 20:18:35.489606+0800 Test__attribute__[1624:33591] 'Main' 函数开始执行
2017-04-21 20:18:35.489937+0800 Test__attribute__[1624:33591] 使用‘destructor’修饰函数,将执行在 ‘main’ 函数之后
Program ended with exit code: 0

__attribute__((constructor)) 是如何工作的?详情请看这里

deprecated

使用 deprecated 属性修饰函数,用于警告该函数已经被 废弃 不推荐使用。在 iOS 开发过程中很常见,如果调用某些接口,也会出现类似的警告,如:

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
void deprecatedFunction(void) __attribute__((deprecated("此方法已不推荐使用!")));
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
deprecatedFunction();
}
return 0;
}

unavailable

使用 unavailable 属性修饰函数,告诉该函数不可用。

PersonObject.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
// PersonObject.h
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PersonObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) BOOL sex;
@property (nonatomic, assign) NSInteger age;
- (void)testDeprecated __attribute__((deprecated));
- (void)testUnavailable __attribute__((unavailable));
@end

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PersonObject.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
PersonObject *person = [[PersonObject alloc] init];
[person testDeprecated];
[person testUnavailable];
}
return 0;
}

nonnull

使用 nonnull 属性修饰函数,告诉编译器对函数参数进行 NULL 的检查。

PersonObject.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
// PersonObject.h
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PersonObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) BOOL sex;
@property (nonatomic, assign) NSInteger age;
- (void)testNonnull1:(id)avg1 avg2:(id)avg2 __attribute__((nonnull));
- (void)testNonnull2:(nonnull id)avg1 avg2:(nonnull id)avg2;
@end

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PersonObject.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
PersonObject *person = [[PersonObject alloc] init];
[person testNonnull1:@"" avg2:nil];
[person testNonnull2:nil avg2:@""];
}
return 0;
}

区别:

  1. __attribute__((nonnull)) 用于控制连续几个参数不能为空。
  2. nonnull 用于控制单个参数不能为空,控制单个参数时更灵活

noreturn

定义有返回值的函数时,而实际情况有可能没有返回值,此时编译器会报错。

  1. 使用 noreturn 属性修饰函数,告诉编译器该函数不会返回(会直接结束)。
  2. 编译器可以忽略函数调用完成的处理,从而进行相应的优化。
  3. 多用于强制函数退出、死循环等情况。如系统提供的 abort()exit() 函数。

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
void __attribute__((noreturn)) NoReturn1()
{
printf("'NoReturn1' 函数没有返回,有‘noreturn’修饰,会直接结束!\n");
}
void NoReturn2()
{
printf("'NoReturn2' 函数没有返回,没有‘noreturn’修饰,并不会直接结束!\n");
}
int testNoReturn(int state)
{
if (1 == state)
{
NoReturn1();
return 1;
}
else if (2 == state)
{
NoReturn2();
return 2;
}
else
{
return 0;
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
int ret1 = testNoReturn(1);
int ret2 = testNoReturn(2);
int ret3 = testNoReturn(0);
NSLog(@"ret1 = %d, ret2 = %d, ret3 = %d", ret1, ret2, ret3);
}
return 0;
}

运行结果:

1
2
3
4
5
2017-04-21 21:31:00.787175+0800 Test__attribute__[3863:120161] 'Main' 函数开始执行
'NoReturn1' 函数没有返回,有‘noreturn’修饰,会直接结束!
'NoReturn2' 函数没有返回,没有‘noreturn’修饰,并不会直接结束!
2017-04-21 21:31:00.787541+0800 Test__attribute__[3863:120161] ret1 = 0, ret2 = 2, ret3 = 0
Program ended with exit code: 0

从运行结果 ret1 的值为 0,可以看出函数没有返回值直接退出了。

cleanup

修饰变量

使用 cleanup 属性修饰变量,告诉编译器,在变量的作用域结束时执行指定的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
static void stringCleanUp(NSString **string)
{
NSLog(@"%@", *string);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
NSString *string __attribute__((cleanup(stringCleanUp))) = @"Hello World !";
NSLog(@"string = %@", string);
NSLog(@"'Main' 函数执行结束");
}
return 0;
}

运行结果:

1
2
3
4
5
2017-04-21 21:55:48.283879+0800 Test__attribute__[4556:147371] 'Main' 函数开始执行
2017-04-21 21:55:48.284017+0800 Test__attribute__[4556:147371] string = Hello World !
2017-04-21 21:55:48.284043+0800 Test__attribute__[4556:147371] 'Main' 函数执行结束
2017-04-21 21:55:48.284060+0800 Test__attribute__[4556:147371] Hello World !
Program ended with exit code: 0

修饰 Block

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
static void blockCleanUp(__strong void(^*block)(void))
{
(*block)();
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
__strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^{
NSLog(@"Hello World!");
};
block();
NSLog(@"'Main' 函数执行结束");
}
return 0;
}

运行结果:

1
2
3
4
5
2017-04-21 22:10:25.290850+0800 Test__attribute__[4901:163032] 'Main' 函数开始执行
2017-04-21 22:10:25.291020+0800 Test__attribute__[4901:163032] Hello World!
2017-04-21 22:10:25.291046+0800 Test__attribute__[4901:163032] 'Main' 函数执行结束
2017-04-21 22:10:25.291057+0800 Test__attribute__[4901:163032] Hello World!
Program ended with exit code: 0

提示:void(^block)(void) 的指针是 void(^*block)(void)

宏定义 Block

可以将成对出现的代码写在一起,如 NSLock的使用

1
2
// 解锁 Macro
#define UN_LOCK_BLOCK __strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^

PersonObject.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//
// PersonObject.h
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PersonObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) BOOL sex;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSLock *lock;
@end

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//
// main.m
// Test__attribute__
//
// Created by shenyuanluo on 2017/4/21.
// Copyright © 2017年 shenyuanluo. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PersonObject.h"
// 解锁 Macro
#define UN_LOCK_BLOCK __strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^
static void blockCleanUp(__strong void(^*unLockBlock)(void))
{
(*unLockBlock)();
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"'Main' 函数开始执行");
PersonObject *person = [[PersonObject alloc] init];
[person.lock lock];
NSLog(@"进入‘Person’的临界区!");
UN_LOCK_BLOCK {
NSLog(@"解锁操作执行!");
[person.lock unlock];
};
/**
* 只想相关临界区的操作
*/
NSLog(@"'Person'临界区操作代码。。。");
NSLog(@"'Main' 函数执行结束");
}
return 0;
}

运行结果:

1
2
3
4
5
6
2017-04-21 22:30:38.815186+0800 Test__attribute__[4987:166666] 'Main' 函数开始执行
2017-04-21 22:30:38.815357+0800 Test__attribute__[4987:166666] 进入‘Person’的临界区!
2017-04-21 22:30:38.815400+0800 Test__attribute__[4987:166666] 'Person'临界区操作代码!
2017-04-21 22:30:38.815417+0800 Test__attribute__[4987:166666] 'Main' 函数执行结束
2017-04-21 22:30:38.815426+0800 Test__attribute__[4987:166666] 解锁操作执行!
Program ended with exit code: 0

参考资料

  1. 黑魔法__attribute__((cleanup))
  2. Mattt Thompson 的__attribute__
  3. 神奇的attribute
  4. Objective-C Runtime 运行时之一:类与对象
------ 本文结束 ------
欣赏此文?求鼓励,求支持!