神奇的__attribute__

 

__attribute__是GNU C特色之一,在iOS用的比较广泛。如果你没有用过,那系统库你总用过,在Foundation.framework中有很多地方用到__attribute__特性。

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

接下来就从iOS中常见用法谈起。

1. format

作用:编译器会检查格式化字符串与“...”的匹配情况,防止产生难以发现的Bug

用法:

__attribute__((format(printf,m,n)))

__attribute__((format(scanf,m,n)))

其中参数m与n的含义为:

m 格式化字符串(format string)的位置(顺序从1开始);

n 参数“…”的位置(顺序从1开始);

例子:

FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
#define kMaxStringLen 512

extern void MyLog(const char *tag,const char *format,...) __attribute__((format(printf,2,3)));

void MyLog(const char *tag,const char *format,...) {
    va_list ap;
    va_start(ap, format);
    
    char* pBuf = (char*)malloc(kMaxStringLen);
    if (pBuf != NULL)
    {
        vsnprintf(pBuf, kMaxStringLen, format, ap);
    }
    va_end(ap);
    
    printf("TAG:%s Message:%s",tag,pBuf);

    free(pBuf);
}

2. deprecated

作用:使编译会给出过时的警告

用法:

__attribute__((deprecated))

__attribute__((deprecated(s)))

例子:

#define DEPRECATED_ATTRIBUTE  __attribute__((deprecated))
#if __has_feature(attribute_deprecated_with_message)
  #define DEPRECATED_MSG_ATTRIBUTE(s)  __attribute__((deprecated(s)))
#else
  #define DEPRECATED_MSG_ATTRIBUTE(s)  __attribute__((deprecated))
#endif      

3. availability

作用:指明API版本的变更

用法:

__attribute__((availability(macosx,introduced=m,deprecated=n)))

m 引入的版本

n 过时的版本

例子:

#define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __attribute__((availability(ios,introduced=_iosIntro,deprecated=_iosDep,message="" __VA_ARGS__)))

4. unavailable

作用:告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过init来初始化,只能通过特定的初始化方法,就可以将init方法标记为unavailable。

用法:

__attribute__((unavailable))

例子:

#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))

#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
#import <Foundation/Foundation.h>

@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

066226DB-F65E-4763-B21A-7D8E04B151FF.png

5. const

作用:用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外, 其它只需要返回第一次的结果就可以了,进而可以提高效率

      该属性主要适用于没有静态状态和副作用的一些函数,并且返回值仅仅依赖输入的参数。(const参数不能用在带有指针类型参数的函数中,因为该属性不但影响函数的参数值,同样也影响到了参数指向的数据,它可能会对代码本身产生严重甚至是不可恢复的严重后果。)

用法:

__attribute__((const))

例子:

int  __attribute__((const)) add(int x)
{
    printf("%s(%d)\n", __FUNCTION__, x);
    return x + 1;
}
 
int  add2(int x)
{
    printf("%s(%d)\n", __FUNCTION__, x);
    return x + 1;
}
 
int main(int argc, char* argv[])
{
    int i, j;
 
    i = add(10);
    j = add(10);
 
    printf("%d %d\n", i, j);
 
    i = add2(10);
    j = add2(10);
 
    printf("%d %d\n", i, j);
 
    return 0;
}

6. cleanup

作用:离开作用域之后执行指定的方法。实际应用中可以在作用域结束之后做一些特定的工作,比如清理。

用法 :__attribute__((cleanup(...)))

例子:


static void stringCleanUp(__strong NSString **string) {
    NSLog(@"%@", *string);
}

void testCleanUp() {
    __strong NSString *string __attribute__((cleanup(stringCleanUp))) = @"stringCleanUp";
}

static void blockCleanUp(__strong void(^ *block)()) {
    if (*block) {
        (*block)();
    }
}

void testBlockCleanUp() {
    __strong void(^block)() __attribute__((cleanup(blockCleanUp))) = ^{
        NSLog(@"block");
    };
}

static void lambdaCleanUp(void (**lambda)()) {
    if (*lambda) {
        (*lambda)();
    }
}

void testLambdaCleanUp() {
    void (*lambda)() __attribute__((cleanup(lambdaCleanUp))) = []() {
        puts("lambda");
    };
}

int main(int argc, char * argv[]) {
   @autoreleasepool {
      testCleanUp();
    
      testBlockCleanUp();
    
      testLambdaCleanUp();
    
   }
 return 0;
}
//结合宏定义使用
#define BlockCleanUp __strong void(^block)() __attribute__((cleanup(blockCleanUp))) = ^
#define LambdaCleanUp void (*lambda)() __attribute__((cleanup(lambdaCleanUp))) = []()
void testDefine() {
    BlockCleanUp {
        puts("BlockCleanUp");
    };
    
    LambdaCleanUp{
        puts("LambdaCleanUp");
    };
}

7. constructor与destructor

作用:

__attribute__((constructor)) 在main函数之前执行,

__attribute__((destructor)) 在main函数之后执行。

__attribute__((constructor(PRIORITY)))和__attribute__((destructor(PRIORITY)))按优先级执行。

 

(可用于动态库注入的Hook)

用法:
__attribute__((constructor))

__attribute__((destructor))

__attribute__((constructor(PRIORITY)))

__attribute__((destructor(PRIORITY)))

PRIORITY 为优先级

例子:

void __attribute__((constructor))  start() {
    NSLog(@"%s",__FUNCTION__);
}

void __attribute__((destructor)) end() {
     NSLog(@"%s",__FUNCTION__);
}


![Uploading E71D5B89-60DF-47C6-A923-F731680F25B6_088963.png . . .]int main(int argc, char * argv[]) {
    
    NSLog(@"%s",__FUNCTION__);
    
    return 0;
}

E71D5B89-60DF-47C6-A923-F731680F25B6.png

void __attribute__((constructor)) start() {
    NSLog(@"%s",__FUNCTION__);
}

void __attribute__((constructor(100)))  start100() {
    NSLog(@"%s",__FUNCTION__);
}

void __attribute__((constructor(101)))  start101() {
    NSLog(@"%s",__FUNCTION__);
}

void __attribute__((destructor)) end() {
    NSLog(@"%s",__FUNCTION__);
}

void __attribute__((destructor)) end100() {
     NSLog(@"%s",__FUNCTION__);
}

void __attribute__((destructor)) end101() {
    NSLog(@"%s",__FUNCTION__);
}

int main(int argc, char * argv[]) {
    
    NSLog(@"%s",__FUNCTION__);
    
    return 0;
}

46182749-3804-40A2-ACA6-691BB2E22B71.png

8. noreturn

作用:定义有返回值的函数时,而实际情况有可能没有返回值,此时编译器会报错。加上attribute((noreturn))则可以很好的处理类似这种问题。

用法:
__attribute__((noreturn))

例子:

void __attribute__((noreturn)) onExit();

int test(int state) {
    if (state == 1) {
        onExit();
    }else {
        return 0;
    }
}

9. nonnull

作用:编译器对函数参数进行NULL的检查

用法:__attribute__((nonnull(...)))

extern void *my_memcpy_2 (void *dest, const void *src, size_t len) __attribute__((nonnull (1, 2)));

extern void *my_memcpy_3 (void *dest, const void *src, const void *other, size_t len) __attribute__((nonnull (1, 2, 3)));

void test_my_memcpy() {
    my_memcpy_2(NULL, NULL, 0);
    my_memcpy_3("", "", NULL, 0);
}

1CA959CA-3710-4F9B-AFC5-A4F263811F6D.png

10. aligned 与 packed

作用:aligned(m) 将强制编译器尽其所能地确保变量在分配空间时采用m字节对齐方式packed该属性对struct 或者union 类型进行定义,设定其类型的每一个变量的内存约束,当用在enum 类型定义时,暗示了应该使用最小完整的类型。aligned 属性使被设置的对象占用更多的空间,使用packed 可以减小对象占用的空间。

用法:
attribute ((aligned (m)))

attribute ((aligned))

attribute ((packed))

例子:

//运行在iPhone5模拟器上
struct p {
    int a;
    char b;
    short c;
}__attribute__((aligned(4))) pp;

struct m {
    char a;
    int b;
    short c;
}__attribute__((aligned(4))) mm;

struct o {
    int a;
    char b;
    short c;
}oo;

struct x {
    int a;
    char b;
    struct p px;
    short c;
 }__attribute__((aligned(8))) xx;

struct MyStruct {
    char c;
    int  i;
    short s;
}__attribute__ ((__packed__));

struct MyStruct1 {
    char c;
    int  i;
    short s;
}__attribute__ ((aligned));

struct MyStruct2 {
    char c;
    int  i;
    short s;
}__attribute__ ((aligned(4)));

struct MyStruct3 {
    char c;
    int  i;
    short s;
}__attribute__ ((aligned(8)));

struct MyStruct4 {
    char c;
    int  i;
    short s;
}__attribute__ ((aligned(16)));

int main(int argc, char * argv[]) {
    
    printf("sizeof(int)=%lu,sizeof(short)=%lu.sizeof(char)=%lu\n",sizeof(int),sizeof(short),sizeof(char));
    
    printf("pp=%lu,mm=%lu \n", sizeof(pp),sizeof(mm));
    
    printf("oo=%lu,xx=%lu \n", sizeof(oo),sizeof(xx));

    printf("mystruct=%lu \n", sizeof(struct MyStruct));
    
    printf("mystruct1=%lu \n", sizeof(struct MyStruct1));
    
    printf("mystruct2=%lu \n", sizeof(struct MyStruct2));
    
    printf("mystruct3=%lu \n", sizeof(struct MyStruct3));
    
    printf("mystruct4=%lu \n", sizeof(struct MyStruct4));
    
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

DD0CE993-E373-410E-B0CC-5F91C0E56729.png

 

#################################
 

GCC

format

format属性指定了一个函数像printf,scanf,strftime或者strfmo风格的参数,这个参数应该是可以进行类型检查的一个格式化字符串。

 

1
2
3
extern int
my_printf (void *my_object, const char *my_format, ...)
  __attribute__((format(printf, 2, 3)));

Objective-C程序员也可使用__NSString__来格式化来做到相同的格式化规则,像在NNString中通过 +stringWithFormat: 和 NSLog()格式化字符串一样。

nonnull

这个nonnull属性指定了某些函数的参数必须是非空的指针。

 

1
2
3
extern void *
my_memcpy (void *dest, const void *src, size_t len)
  __attribute__((nonnull (1, 2)));

使用nonnull编码期望这个值遵守一个明确的约定中,这样能帮助捕获潜伏在任何代码调用的NULL指针bugs,请记住:编译时的错误 >> 运行时的错误。

noreturn

一些标准库函数,例如abortexit,是不能返回的。GCC自动知道这些东西,这个noreturn属性用于指定任何其他函数永远不会返回的情况。

例如,AFNetworking 使用noreturn属性在它的网络请求线程进入点的方法里面,这个方法用在当大量产生专用的网络的线程里用来保证分离的线程持续执行在应用的整个生命周期中。

pure/const

pure属性指定了一个函数除了返回值没有副作用,例如它的返回值仅仅依赖参数和/或者全局变量。这样的函数可以用公共子表达式消除并且循环优化就像一个算数操作符那样。

pure属性指定了一个函数不会检查任何值除了它们的参数,并且返回值没有副作用。注意到一个函数有一个指针参数并且需呀检查数据的指向不能声明成const。同样的,一个函数调用一个非nonst函数通常不能为const,一个const函数返回void并没有什么意义。

 

1
int square(int n) __attribute__((const));

pureconst是两个执行在一个函数式编程惯例中的参数为了允许有效性能优化。const可以被认为是严格形式的pure因为它不依赖全局变量或者指针。

例如,因为一个函数声明为const的结果并不依赖任何东西除了传进来的参数。函数的结果能够缓存那个结果并且当函数被调用时返回,这样的函数叫做相同的组合参数(也就是说,我们知道一个数字的平方是一个常量,所以我们仅仅需要只计算它一次)。

unused

这个属性,附着在一个函数后面,意味着那个函数很可能不会被使用,GCC不会对这个函数产生警告。

__unused关键词可以获得相同的效果,声明这个在方法实现中不会被使用的参数中。知道那以后一些上下文就可以允许编译器来做相应的优化。你很可能喜欢在delegate方法实现李勉使用__unused,因为协议频繁的提高更多的上下文比通常必要的情况,为了满足大量的潜在使用案例。

LLVM

像GCC的很多特征一样,Clang也支持__attribute__,添加到它自己的小范围的扩展。为了检查某个属性的可用性,你可以直接使用__has_attribute属性。

availability

Clang引进了availability属性,这个可以被取代在声明描述的生命周期中声明相对于操作系统的版本。思考对一个简单函数f:的函数声明

 

1
void f(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7)));

availability属性声明f在OS X老虎系统中被引入,在OS X雪豹系统中被弃用,在OS X 山狮系统中被废弃。

这个信息被Clang用来决定什么时候使用f:函数式安全的,例如,假如Clang在OS X 美洲豹系统上编译,调用f()函数将成功。假如Clang在OS X雪豹系统中编译,函数调用将成功但是Clang会发出一个警告指明这个函数被弃用了。最后,假如Clang被引进编译OS X山狮系统的代码,函数调用将失败,因为f()函数已经不再可用了。

availability属性是一个逗号分隔的列表以平台名开始然后引入一些定语列举出生命周期内的重要里程碑事件附加额外的信息(以任何顺序)。

  • introduced:声明被引入的第一个版本
  • deprecated:声明被弃用的第一个版本,这意味着用户应该把这个API移走
  • obsoleted: 声明被废弃的第一个版本,这意味着它将被完全移除并且不能再使用
  • unavailable:声明在这个平台上将永远不可用
  • message:额外的消息将被Clang提供当忽略一个警告或者一个错误在使用一个被弃用或者被废弃的声明。对引导用户替换APIs很有用。

在声明时可以使用多个availability属性,每个对应不同的平台,仅当availability属性对应相应的目标平台被使用的时候,任何其他才将被忽略。假如没有availability 属性指定可用性对现在的目标平台,availability 属性将被忽略。

支持的平台:

  • ios:苹果的iOS操作系统。最小的部署目标被指定通过-mios-version-min=*version*或者-miphoneos-version-min=*version*命令行参数。
  • macosx:苹果的OS X 操作系统,最小的部署目标被指定通过-mmacosx-version-min=*version*命令行参数

overloadable

Clang提供对C++函数在C中重载的支持。在C中函数重载被引进使用overloadable属性。例如,一个可能提供一个重载版本的tgsin函数来精确执行相关的标准函数计算float,double,long double的正弦值:

 

1
2
3
4
#include <math.h>
float __attribute__((overloadable)) tgsin(float x) { return sinf(x); }
double __attribute__((overloadable)) tgsin(double x) { return sin(x); }
long double __attribute__((overloadable)) tgsin(long double x) { return sinl(x); }

请注意overloadable只对函数起作用。你可以重载方法声明在某种范围内通过使用通用的返回值和参数类型,想id或者void *.


上下文是国王当它遇到编译器优化时。通过提供限制在怎样解析你的代码,增加你参数尽可能高效代码的可能性。遇到编译器把你打断,这将是一项奖励。

还有__attribute__并不仅仅对编译器有用:下一个人看代码也将感谢这些额外的上下文。所以多走几英尺远将对你的合作中和接替者或者从现在算2年以后的你(那个时候你已经忘记了所以的事情关于这份代码)自己有用

你付出了多少爱,最终你会得到多少爱。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值