本文目录html
#define kTabBarH (49.0f) #define kScreenH [UIScreen mainScreen].bounds.size.height #define isScreenWidthEqual320 (fabs([UIScreen mainScreen].bounds.size.width - 320) < DBL_EPSILON)
// 例子1:一样是一个表达式 #define isNilOrNull(obj) (obj == nil || [obj isEqual:[NSNull null]]) // 例子2:也能够没有参数值 使用的时候要加上"()",是在使用的时候单独成为一行语句的宏 #define MKAssertMainThread() NSAssert([NSThread isMainThread], @"This method must be called on the main thread")
// do {} while(0) 通常是没有返回值的,仅仅是代码块而已,使用do {} while(0)意思是让它执行一次,至关于直接调用代码块 #define NSAssert(condition, desc, ...) \ do { \ __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ if (!(condition)) { \ NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \ object:self file:__assert_file__ \ lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \ } \ __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ } while(0)
CG_INLINE CGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) { CGRect rect; rect.origin.x = x; rect.origin.y = y; rect.size.width = width; rect.size.height = height; return rect; } NS_INLINE BOOL MKIsLeapYear() { NSCalendar *calendar = [NSCalendar currentCalendar]; NSInteger year = [calendar component:NSCalendarUnitYear fromDate:[NSDate new]]; return year % 100 == 0 ? year % 400 == 0 : year % 4 == 0; }
// 例如能够像以下几种方式使用NSAssert这个宏 NSAssert(10 > 11, @"错误1:%@", @"err desc 1"); NSAssert(10 > 11, @"错误1:%@\n错误2:%@", @"err desc 1", @"err desc 2");
关于宏定义中的#和##的说明
#
有两个做用:
1.将变量直接转化为相应字面量的C语言字符串 如a=10 #a会转化为"10"
2.链接两个C字符串,使用以下ios
#define toString(a) #a // 转为字符串 #define printSquare(x) printf("the square of " #x " is %d.\n",(x)*(x)) // 链接字符串
使用以下macos
printf("%s\n", toString(abc)); // abc printSquare(3); // the square of 3 is 9.
##
的常见用处是链接,它会将在它以前的语句、表达式等和它以后的语句表达式等直接链接。这里有个特殊的规则,在逗号和__VA_ARGS__之间的双井号,除了拼接先后文本以外,还有一个功能,那就是若是后方文本为空,那么它会将前面一个逗号吃掉。这个特性当且仅当上面说的条件成立时才会生效,所以能够说是特例。swift
// 用法1:代码和表达式直接链接 #define combine(a, b) a##b #define exp(a, b) (long)(a##e##b) // 用法2:给表达式加前缀 #define addPrefixForVar(a) mk_##a // 用法3,用于链接printf-like方法的format语句和可变参数 #define format(format, ...) [NSString stringWithFormat:format, ##__VA_ARGS__]
使用示例以下:数组
NSLog(@"%zd", combine(10, 556)); // 10556 NSLog(@"%f", combine(10, .556)); // 10.556000 NSLog(@"%tu", exp(2, 3)); // 2000 // int x = 10; int mk_x = 30; NSLog(@"%d", addPrefixForVar(x)); // 30 NSLog(@"%@", format(@"%@_%@", @"good", @"morning")); // good_morning NSLog(@"%@", format(@"hello")); // hello
对于使用##
链接可变参数的用法,若是不加##
会致使编译没法经过,这是由于: 这个##在逗号和__VA_ARGS__之间,后面的值不存在的时候 会忽略## 前面的,
也就是说:
当有## 调用format(format) 替换为 [NSString stringWithFormat:format]
当没有## 调用format(format) 替换为 [NSString stringWithFormat:format ,]安全
还有一点要注意的是:#
和##
只能用在宏定义中,而不能使用在函数或者方法中。app
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") #define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
也可使用下面的两句函数
#pragma clang assume_nonull begin #pragma clang assume_nonull end
告诉clang编译器这二者之间内容非空ui
例如编码
NS_CLASS_AVAILABLE // 类以前 NS_AVAILABLE(_mac, _ios) // 方法以后 NS_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) // 方法以后 OBJC_SWIFT_UNAVAILABLE("use 'xxx' instead") // 针对swift特殊说明取代它的类和方法
不一样功能的类以前的可用性标记不一样,例如NSAutoreleasePool以前
NS_AUTOMATED_REFCOUNT_UNAVAILABLE @interface NSAutoreleasePool : NSObject{ ...
对于这些标记究竟干了些什么,请看本文第三部分__attribute__。
#define NS_DURING @try { #define NS_HANDLER } @catch (NSException *localException) { #define NS_ENDHANDLER } #define NS_VALUERETURN(v,t) return (v) #define NS_VOIDRETURN return #if __clang__ #define __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wformat-extra-args\"") #define __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS _Pragma("clang diagnostic pop") #else #define __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS #define __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS #endif
#if !defined(NS_BLOCKS_AVAILABLE) #if __BLOCKS__ && (MAC_OS_X_VERSION_10_6 <= MAC_OS_X_VERSION_MAX_ALLOWED || __IPHONE_4_0 <= __IPHONE_OS_VERSION_MAX_ALLOWED) #define NS_BLOCKS_AVAILABLE 1 #else #define NS_BLOCKS_AVAILABLE 0 #endif #endif #if !defined(NS_NONATOMIC_IOSONLY) #if TARGET_OS_IPHONE #define NS_NONATOMIC_IOSONLY nonatomic #else #if __has_feature(objc_property_explicit_atomic) #define NS_NONATOMIC_IOSONLY atomic #else #define NS_NONATOMIC_IOSONLY #endif #endif #endif #if defined(__cplusplus) #define FOUNDATION_EXTERN extern "C" #else #define FOUNDATION_EXTERN extern #endif
(本文在第三部分详细介绍__attribute__.).
例以下面这些常见的宏都属于这种:
// NSLog 方法后面使用的宏 #define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A))) // 这些都是在<Foundation/NSObjCRuntime.h>中定义的宏 #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) #define NS_RETURNS_INNER_POINTER __attribute__((objc_returns_inner_pointer)) #define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode"))) #define NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE __attribute__((objc_arc_weak_reference_unavailable)) #define NS_REQUIRES_PROPERTY_DEFINITIONS __attribute__((objc_requires_property_definitions)) #define NS_REPLACES_RECEIVER __attribute__((ns_consumes_self)) NS_RETURNS_RETAINED #define NS_RELEASES_ARGUMENT __attribute__((ns_consumed)) #define NS_VALID_UNTIL_END_OF_SCOPE __attribute__((objc_precise_lifetime)) #define NS_ROOT_CLASS __attribute__((objc_root_class)) #define NS_REQUIRES_SUPER __attribute__((objc_requires_super)) #define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) #define NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION __attribute__((objc_protocol_requires_explicit_implementation)) #define NS_CLASS_AVAILABLE(_mac, _ios) __attribute__((visibility("default"))) NS_AVAILABLE(_mac, _ios) #define NS_CLASS_DEPRECATED(_mac, _macDep, _ios, _iosDep, ...) __attribute__((visibility("default"))) NS_DEPRECATED(_mac, _macDep, _ios, _iosDep, __VA_ARGS__) #define NS_SWIFT_NOTHROW __attribute__((swift_error(none))) #define NS_INLINE static __inline__ __attribute__((always_inline)) #define NS_REQUIRES_NIL_TERMINATION __attribute__((sentinel(0,1)))
而咱们常常看到的这样的几个宏
#define NS_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios) #define NS_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac) #define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
这三个其实被定义在了<CoreFoundation/CFAvailability.h>中
#define CF_AVAILABLE(_mac, _ios) __attribute__((availability(macosx,introduced=_mac))) #define CF_AVAILABLE_MAC(_mac) __attribute__((availability(macosx,introduced=_mac))) #define CF_AVAILABLE_IOS(_ios) __attribute__((availability(macosx,unavailable)))
总之你会发现只要是不具有表达式或者代码块功能的宏,绝大多数都是由__attribute__定义的,那么__attribute__究竟是什么呢,请继续:
GNU C 的一大特点就是__attribute__机制。__attribute__能够设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
__attribute__的书写方式是:__attribute__后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数,格式如:
__attribute__((attribute-list))
其位置约束为:放于声明的尾部“;” 以前
。
那么如今的问题就是什么状况下使用__attribute__,以及如何设置参数attribute-list,主要分为三种状况:
函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
函数属性能够帮助开发者把一些特性添加到函数声明中,从而可使编译器在错误检查方面的功能更强大。__attribute__机制也很容易同非GNU应用程序作到兼容之功效。
例如NSLog声明中使用到的宏:
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
__attribute__((format(__NSString__, F, A)))
该__attribute__属性能够给被声明的函数加上相似printf或者scanf的特征,它可使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤为是处理一些很难发现的bug。对于format参数的使用以下
format (archetype, string-index, first-to-check)
第一参数须要传递“archetype”指定是哪一种风格;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定第一个可变参数所在的索引。
如NSLog对这个宏的使用以下:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
那么它如何进行语法检查,看下面的例子:
extern void myPrint(const char *format,...)__attribute__((format(printf,1,2))); void test() { myPrint("i=%d\n",6); myPrint("i=%s\n",6); myPrint("i=%s\n","abc"); myPrint("%s,%d,%d\n",1,2); }
使用clang命令编译一下:
clang -c main.m
结果会出现下面的警告
main.m:70:22: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat] myPrint("i=%s\n",6); ~~ ^ %d main.m:72:26: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat] myPrint("%s,%d,%d\n",1,2); ~~ ^ %d main.m:72:21: warning: more '%' conversions than data arguments [-Wformat] myPrint("%s,%d,%d\n",1,2); ~^ 3 warnings generated.
若是将
__attribute__((format(printf,1,2)))直接去掉,再次编译,则不会有任何警告。
该属性通知编译器函数从不返回值。当遇到相似函数还未运行到return语句就须要退出来的状况,该属性能够避免出现错误信息。C库函数中的abort()和exit()的声明格式就采用了这种格式。使用以下:
void fatal () __attribute__ ((noreturn)); void fatal (/* ... */) { /* ... */ /* Print error message. */ /* ... */ exit (1); }
若函数被设定为constructor属性,则该函数会在main()函数执行以前被自动的执行。相似的,若函数被设定为destructor属性,则该 函数会在main()函数执行以后或者exit()被调用后被自动的执行。拥有此类属性的函数常常隐式的用在程序的初始化数据方面。
例如:
static __attribute__((constructor)) void before() { printf("Hello"); } static __attribute__((destructor)) void after() { printf(" World!\n"); } int main(int argc, const char * argv[]) { printf("0000"); return 0; }
程序输出结果是:
Hello0000 World!
若是多个函数使用了这个属性,能够为它们设置优先级来决定执行的顺序:
__attribute__((constructor(PRIORITY))) __attribute__((destructor(PRIORITY)))
如:
static __attribute__((constructor(101))) void before1() { printf("before1\n"); } static __attribute__((constructor(102))) void before2() { printf("before2\n"); } static __attribute__((destructor(201))) void after1() { printf("after1\n"); } static __attribute__((destructor(202))) void after2() { printf("after2\n"); } int main(int argc, const char * argv[]) { printf("0000\n"); return 0; }
输出的结果是:
before1 before2 0000 after2 after1
从输出的信息看,前处理都是按照优先级前后执行的,然后处理则是相反的.
须要注意的是:优先级是有范围的。0-100(包括100),是内部保留的,因此在编码的时候须要注意.
另一个注意的点是,上面的函数没有声明而是直接实现这样__attribute__就须要放到前面,应该多使用函数声明和实现分离的方法:
static void before1() __attribute__((constructor(1))); static void before1() { printf("before1\n"); }
其余的函数属性(Function Attribute )还有:noinline, always_inline, pure, const, nothrow, sentinel, format, format_arg, no_instrument_function, section, constructor, destructor, used, unused, deprecated, weak, malloc, alias, warn_unused_result, nonnull等等,能够参考 GNU C Function Attribute查看它们的具体使用方法。
如何同时使用多个属性:
能够在同一个函数声明里使用多个__attribute__,而且实际应用中这种状况是十分常见的。只须要把它们用','隔开就能够,如:
__attribute__((noreturn, format(printf, 1, 2)))
该属性设定一个指定大小的对齐格式(以字节 为单位),例如:
struct S { short f[3]; } __attribute__ ((aligned (8))); typedef int more_aligned_int __attribute__ ((aligned (8)));
该声明将强制编译器确保(尽它所能)变量类型为struct S 或者more_aligned_int的变量在分配空间时采用至少8字节对齐方式。
如上所述,你能够手动指定对齐的格式,一样,你也可使用默认的对齐方式。若是aligned 后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器状况使用最大最有益的对齐方式。例如:
struct S { short f[3]; } __attribute__ ((aligned));
这里,若是sizeof(short)的大小为2(byte),那么,S的大小就为6。取一个2的次方值,使得该值大于等于6,则该值为8,因此编译器将设置S类型的对齐方式为8字节。
aligned 属性使被设置的对象占用更多的空间,相反的,使用packed 能够减少对象占用的空间。
须要注意的是,attribute属性的效力与你的链接器也有关,若是你的链接器最大只支持16字节对齐,那么你此时定义32 字节对齐也是无济于事的。
使用该属性对struct或者union类型进行定义,设定其类型的每个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类型(it indicates that the smallest integral type should be used)。
下面的例子中,packed_struct 类型的变量数组中的值将会牢牢的靠在一块儿,但内部的成员变量s不会被“pack” ,若是但愿内部的成员变量也被packed 的话,unpacked-struct也须要使用packed进行相应的约束。
struct my_unpacked_struct { char c; int i; }; struct my_packed_struct { char c; int i; struct my_unpacked_struct s; }__attribute__ ((__packed__));
下面的例子中使用这个属性定义了一些结构体及其变量,并给出了输出结果和对结果的分析。
程序代码为:
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; int main() { printf("sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%d\n",sizeof(int),sizeof(short),sizeof(char)); printf("pp=%d,mm=%d \n", sizeof(pp),sizeof(mm)); printf("oo=%d,xx=%d \n", sizeof(oo),sizeof(xx)); return 0; }
输出结果为:
sizeof(int)=4,sizeof(short)=2.sizeof(char)=1 pp=8,mm=12 oo=8,xx=24
分析:
1.sizeof(pp):
sizeof(a)+sizeof(b)+sizeof(c)=4+1+1=6<8 因此sizeof(pp)=8
2.sizeof(mm):
sizeof(a)+sizeof(b)+sizeof(c)=1+4+2=7
可是a后面须要用3个字节填充,可是b是4个字节,因此a占用4字节,b占用4 个字节,而c又要占用4个字节。因此sizeof(mm)=12
3.sizeof(oo):
sizeof(a)+sizeof(b)+sizeof(c)=4+1+2=7
由于默认是以4字节对齐,因此sizeof(oo)=8
4.sizeof(xx):
sizeof(a)+ sizeof(b)=4+1=5
sizeof(pp)=8; 即xx是采用8字节对齐的,因此要在a,b后面添3个空余字节,而后才能存储px,
4+1+(3)+8+1=17
由于xx采用的对齐是8字节对齐,因此xx的大小一定是8的整数倍,即xx的大小是一个比17大又是8的倍数的一个最小值,17<24,因此sizeof(xx)=24.
其余的函数属性(Function Attribute )还有:aligned, packed, transparent_union, unused, deprecated and may_alias等等,能够参考GNU C Function Attribute查看它们的具体使用方法。
变量属性最经常使用的是aligned和packed(和上面介绍的用法相同),其余变量属性的用法请参考GNU C Variable Attribute。
如同GCC编译器, Clang也支持 __attribute__, 并且对它进行的一些扩展.
若是要检查指定属性的可用性,你可使用__has_attribute指令。如:
#if defined(__has_feature) #if __has_attribute(availability) // 若是`availability`属性可使用,则.... #endif #endif
下面介绍一些clang特有的attribute
该属性描述了方法关于操做系统版本的适用性, 使用以下:
(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7)));
他表示被修饰的方法在首次出如今 OS X Tiger(10.4),在OS X Snow Leopard(10.6)中废弃,在 OS X Lion(10.7)移除。
clang能够利用这些信息决定何时使用它使安全的。好比:clang在OS X Leopard()编译代码,调用这个方法是成功的,若是在OS X Snow Leopard编译代码,调用成功可是会发出警告指明这个方法已经废弃,若是是在OS X Lion,调用会失败,由于它再也不可用。
availability属性是一个以逗号为分隔的参数列表,以平台的名称开始,包含一些放在附加信息里的一些里程碑式的声明。
introduced:第一次出现的版本。
deprecated:声明要废弃的版本,意味着用户要迁移为其余API
obsoleted:声明移除的版本,意味着彻底移除,不再能使用它
unavailable:在这些平台不可用
message:一些关于废弃和移除的额外信息,clang发出警告的时候会提供这些信息,对用户使用替代的API很是有用。
这个属性支持的平台:
ios,macosx。
clang 在C语言中实现了对C++函数重载的支持,使用overloadable属性能够实现。例如:
#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 *。