OC中的 __attribute__ (人为警告⚠️ 黄色提示)

来源:互联网 发布:ae cc 2017 mac 中文 编辑:程序博客网 时间:2024/06/05 11:07

转自:http://www.jianshu.com/p/529dc0501bd3

引言

在我们编写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/


原创粉丝点击