title: OC中的__attribute__
 date: 2016–05–30 10:37:40

tags:

引言

图一
图二

在我们编写OC代码的时候经常可以看到这样的警告
 一个是方法被废弃了,一个是我们输入的参数不合理。我们知道 编译时异常,要比运行时异常好的多。
 那么编译器是如何知道这写内容呢?
 我们点击方法,进入头文件中看一下。

FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
//注意后方的宏定义,我们点击过去之后查看一下
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))

看一下这句代码
 
 __attribute__((format(__NSString__,F, A)))
 

 这句的意思是,参数的第F位是格式化字符串,从A位开始我们开始检查
 所以在图二的位置就会有参数不正确的警告。下面我们来系统的认识一下__attribute__

__attribute__ 简单介绍

__attribute__ 是 GNU C 的一大特色。

__attribute__ 语法格式为:

\_\_attribute\_\_ ((attribute-list))

__attribute__ 书写特征是 前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的
 __attribute__ 参数。

其位置约束为:放于声明的尾部“ ;” 之前。
 在 __attribute__ 被加入GC之前还有一个小故事

In fact, when __attribute__ was first introduced to GCC, it was faced with some resistance by some who suggested that #pragma be used exclusively for the same purposes.

There were, however, two very good reasons why __attribute__ was added:

It was impossible to generate #pragma commands from a macro (before the C99 _Pragma operator).
There is no telling what the same #pragma might mean in another compiler.
Quoth the GCC Documentation for Function Attributes:

These two reasons applied to almost any application that might have been proposed for #pragma. It was basically a mistake to use #pragma for anything.

Indeed, if you look at modern Objective-C–in the headers of Apple frameworks and well-engineered open-source projects–__attribute__ is used for myriad purposes. (By contrast, #pragma’s main claim to fame these days is decoration: #pragma mark)

当然上面的 __attribute__ 使用方法是在GCC中使用的在OC中有些是禁用的。下面是我遇到的一些OC中使用的例子和用法。如果您发现了有其他的用法,还请您在下方的评论告诉我。去我的微博@我

__attribute__((format()))

//C中的使用方法
extern int my_printf (void *my_object, const char *my_format, ...) __attribute__((format(printf, 2, 3)));
//这个的意思是第二个参数my_format参数是一个格式化字符串,从第三个参数开始检查
//在Objective-C 中我们使用__string来禁代替format NSString +stringWithFormat: 和 NSLog()都是一个很好的例子
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
+ (instancetype)stringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);

__attribute__((noreturn))

一些标准库函数,如中止和退出,不能返回。
 noreturn属性指定了其他函数,它永远不会返回。
 例如AFNetworking中就有这个用法

//AFURLConnectionOperation.m
+ (void) __attribute__((noreturn)) networkRequestThreadEntryPoint:(id)__unused object {
do {
@autoreleasepool {
[[NSRunLoop currentRunLoop] run];
}
} while (YES);
}

__attribute__((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 从哪个版本开始弃用
//警告的消息
//其实还可以再加一个参数例如
void f(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7)));
//obsoleted完全禁止使用的版本

在swift中也有类似的用法

@available(iOS 6.0, *)
public var minimumScaleFactor: CGFloat // default is 0.0

__attribute__((unused ))

unused waringPicture

这个关键字的含义:如果某个函数使用了这个关键字,那么函数在被调用的时候,要检查或者使用返回值,某则编译器会进行警告。
 使用场合:在把一些功能封装起来(或者SDK的编写)时候,如果对返回值的使用比较重要,那么使用这个关键字提醒编译器要检查返回值是否被利用。
 当我们将返回值赋予一个变量使用时就不会有waring了

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib
BOOL result = [self TestFunc:0];
result = YES;
}
-(BOOL)TestFunc:(NSInteger) num __attribute__ ((warn_unused_result)){
return num > 0?YES:NO;
}

__attribute__((constructor)) 在main函数之前的调用

请看下面的代码

#include<stdio.h> 
__attribute__((constructor)) void before_main() {
printf("before main\n");
}

__attribute__((destructor)) void after_main() {
printf("after main\n");
}

int main(int argc, char **argv) {
printf("in main\n");
return 0;
}

输出结果如下

before main
in main
after main

__attribute__((constructor)) //确保此函数在 在main函数被调用之前调用
 __attribute__((destructor)) // 确保此函数在 在main函数被调用之后调

__attribute__((cleanup())) 用于修饰一个变量,在它的作用域结束时可以自动执行一个指定的方法

在看mantle的源码是看到了这种用法

typedef void (^mtl_cleanupBlock_t)();

#define metamacro_concat_(A, B) A ## B

#define metamacro_concat(A, B) \
metamacro_concat_(A, B)

#define onExit \
try {} @finally {} \
__strong mtl_cleanupBlock_t metamacro_concat(mtl_exitBlock_, __LINE__) __attribute__((cleanup(mtl_executeCleanupBlock), unused)) = ^


+ (void)enumeratePropertiesUsingBlock:(void (^)(objc_property_t property, BOOL *stop))block {
Class cls = self;
BOOL stop = NO;

while (!stop && ![cls isEqual:MTLModel.class]) {
unsigned count = 0;
objc_property_t *properties = class_copyPropertyList(cls, &count);

cls = cls.superclass;
if (properties == NULL) continue;
//注意这里的用法
@onExit {
free(properties);
};

for (unsigned i = 0; i < count; i++) {
block(properties[i], &stop);
if (stop) break;
}
}
}
//@onExit 宏展开之后
@try {} @finally {}
__strong mtl_cleanupBlock_t mtl_exitBlock___LINE__ __attribute__((cleanup(mtl_executeCleanupBlock), unused)) = ^{
free(properties);
};
//可以保证 程序在即将运行出 propertties的作用时释放 properties

随后在网上搜了一下看到了我就叫Sunny怎么了 的一篇博客,非常感谢sunny的分享。
 他的博客中提到了Reactive Cocoa中相同的用法。
 我把他博客中的一些内容摘抄如下

// 指定一个cleanup方法,注意入参是所修饰变量的地址,类型要一样
// 对于指向objc对象的指针(id *),如果不强制声明__strong默认是__autoreleasing,造成类型不匹配
static void stringCleanUp(__strong NSString **string) {
NSLog(@"%@", *string);
}
// 在某个方法中:
{
__strong NSString *string __attribute__((cleanup(stringCleanUp))) = @"sunnyxx";
__strong NSString *__string __attribute__((cleanup(stringCleanUp))) = @"sunnyxx2222";
} // 当运行到这个作用域结束时,自动调用stringCleanUp

//输出是sunnyxx2222 sunnyxx
假如一个作用域内有若干个cleanup的变量,他们的调用顺序是先入后出的栈式顺序;
 而且,cleanup是先于这个对象的dealloc调用的。
既然__attribute__((cleanup(…)))可以用来修饰变量,block当然也是其中之一,写一个block的cleanup函数非常有趣:
// void(^block)(void)的指针是void(^*block)(void)
static void blockCleanUp(__strong void(^*block)(void)) {
(*block)();
}
于是在一个作用域里声明一个block:

{
// 加了个`unused`的attribute用来消除`unused variable`的warning
__strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^{
NSLog(@"I'm dying...");
};
} // 这里输出"I'm dying..."
这里不得不提万能的Reactive Cocoa中神奇的@onExit方法,其实正是上面的写法,简单定义个宏:
#define onExit\
__strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^

用这个宏就能让一些很重要的资源或者IO流等在代码离开作用范围之前释放掉或者关闭掉。


在swift中也有类似的用法
 其实swift中也类似的用法 错误处理(Error Handling)

指定清理操作
 可以使用defer语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的 — — 无论是由于抛出错误而离开,还是由于诸如return或者break的语句。例如,你可以用defer语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。
defer语句将代码的执行延迟到当前的作用域退出之前。该语句由defer关键字和要被延迟执行的语句组成。延迟执行的语句不能包含任何控制转移语句,例如break或是return语句,或是抛出一个错误。延迟执行的操作会按照它们被指定时的顺序的相反顺序执行 — — 也就是说,第一条defer语句中的代码会在第二条defer语句中的代码被执行之后才执行,以此类推。
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// 处理文件。
}
// close(file) 会在这里被调用,即作用域的最后。
}
}
参考

http://nshipster.com/__attribute__/ 
 http://www.cnblogs.com/astwish/p/3460618.html
 http://blog.sunnyxx.com/2014/09/15/objc-attribute-cleanup/

Like what you read? Give Alien Zhang a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.