Clang Language Extensions

XcodeXcodehtml

本文是自《Clang Language Extensions》 中選取部分與 Objective-C 相關的內容翻譯,因爲做者水平有限,如存在理解錯誤或翻譯不到位的地方,還請指正!ios

特性檢查宏(Feature Checking Macros)

__has_builtin

此函數類型的宏傳遞一個函數名做爲參數來判斷該函數是否爲內置函數。macos

#ifndef __has_builtin         // Optional of course.
  #define __has_builtin(x) 0  // Compatibility with non-clang compilers.
#endif

#if __has_builtin(__builtin_trap)
  __builtin_trap();
#else
  abort();
#endif

__has_feature & __has_extension

這兩個函數類型的宏傳遞一個特性的名稱做爲參數,若是該特性同時被 Clang 和當前語言的標準所支持,__has_feature返回 1,不然返回 0。若是該特性被 Clang 和當前語言(不論是該語言的擴展仍是標準)所支持,__has_extension返回 1,不然返回 0。數組

#ifndef __has_feature         // Optional of course.
  #define __has_feature(x) 0  // Compatibility with non-clang compilers.
#endif
#ifndef __has_extension
  #define __has_extension __has_feature // Compatibility with pre-3.0 compilers.
#endif

#if __has_feature(objc_arc)
        NSLog(@"under ARC");
#endif

#if __has_extension(blocks)
        NSLog(@"support blocks");
#endif

出於向後兼容的考慮,__has_feature也能夠用來檢查非標準語法特性,如:不是以c_cxx_objc_等爲前綴的特性。因此用__has_feature(blocks)來檢查是否支持 block 也是能夠的。若是設置-pedantic-errors選項,__has_extension__has_feature做用就是同樣的。xcode

__has_attribute

此宏傳遞一個屬性名稱用於檢查該屬性是否被支持。app

#ifndef __has_attribute         // Optional of course.
  #define __has_attribute(x) 0  // Compatibility with non-clang compilers.
#endif

#if __has_attribute(always_inline)
  #define ALWAYS_INLINE __attribute__((always_inline))
#else
  #define ALWAYS_INLINE
#endif

傳入的屬性名也能夠採用先後加__(下劃線)的命名方式來防止命名衝突,因此這裏__always_inline__always_inline是等價的。函數

文件引入檢查宏(Include File Checking Macros)

並非全部的系統都必定包含你所須要引入的文件,因此你可使用__has_include__has_include_next宏在你#include以前檢查你所須要引入的文件在當前系統是否存在。ui

__has_include

此宏傳入一個你想引入文件的名稱做爲參數,若是該文件可以被引入則返回 1,不然返回 0。this

// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
#include "myinclude.h"
#endif

爲了兼容非 clang 編譯器,你能夠這樣寫:編碼

// To avoid problem with non-clang compilers not having this macro.
#if defined(__has_include)
  #if __has_include("myinclude.h")
    #include "myinclude.h"
  #endif
#endif

__has_include_next

此宏與__has_include功能類似,只不過會判斷引入包含該文件的後面一個路徑下的文件 是否可引入。

// Note the two possible file name string formats.
#if __has_include_next("myinclude.h") && __has_include_next(<stdint.h>)
# include_next "myinclude.h"
#endif

// To avoid problem with non-clang compilers not having this macro.
#if defined(__has_include_next)
#if __has_include_next("myinclude.h")
# include_next "myinclude.h"
#endif
#endif

__has_include_next和 GNU 擴展語句#include_next同樣,只能在頭文件中使用,若是在其餘的位置使用會引發警告。

__has_warning

此宏傳入一個字符串做爲參數,該字符串表示一種警告類型,若是該警告有效返回 true。

#if __has_warning("-Wformat")
...
#endif

內置宏(Builtin Macros)

__BASE_FILE__
返回當前文件的路徑。

__COUNTER__
計數器,初始值爲 0,每次使用__COUNTER__時都會自動 +1。(以文件爲計數單元,即不一樣文件中的__COUNTER__值是獨立計數的)

__INCLUDE_LEVEL__
當前文件被引用的深度,main 文件時該值爲 0。

__TIMESTAMP__
返回當前文件最後一次修改的時間戳。

__clang__
布爾值,返回當前編譯器是否支持 clang。

__clang_major__
返回當前 Clang 的主版本號(如 version4.6.3 中的 4)。

__clang_minor__
返回當前 Clang 的次版本號(如 version4.6.3 中的 6)。

__clang_patchlevel__
返回當前 Clang 的補丁版本號(如 version4.6.3 中的 3)。

__clang_version__
返回當前 Clang 的完整的版本號。

if (__clang__) {
    NSLog(@"__BASE_FILE__: %s", __BASE_FILE__);
    NSLog(@"__COUNTER__: %d", __COUNTER__);
    NSLog(@"__COUNTER__: %d", __COUNTER__);
    NSLog(@"__COUNTER__: %d", __COUNTER__);
    NSLog(@"__TIMESTAMP__: %s", __TIMESTAMP__);
    NSLog(@"__INCLUDE_LEVEL__: %d", __INCLUDE_LEVEL__);
    
    NSLog(@"__clang_major__: %d", __clang_major__);
    NSLog(@"__clang_minor__: %d", __clang_minor__);
    NSLog(@"__clang_patchlevel__: %d", __clang_patchlevel__);
    NSLog(@"__clang_version__: %s", __clang_version__);
}
/*
__BASE_FILE__: /Users/tracy/Desktop/Clang/Clang/main.m
__COUNTER__: 0
__COUNTER__: 1
__COUNTER__: 2
__TIMESTAMP__: Fri Jul 26 14:24:35 2013
__INCLUDE_LEVEL__: 0
__clang_major__: 5
__clang_minor__: 0
__clang_patchlevel__: 0
__clang_version__: 5.0 (clang-500.1.65)
 */

除了以上幾個宏以外,還有兩個是咱們常常會用到了__FUNCTION____LINE__,分別返回當前代碼段所在的函數名和在當前文件中的行數,這個在打印 log 時比較有用。

帶 deprecated 和 unavailable 屬性的方法

方法或屬性能夠加上帶deprecatedunavilable等屬性來表示狀態。如:

void explode(void) __attribute__((deprecated("extremely unsafe, use 'combust' instead!!!")));

若是一個方法被標記爲deprecated,在使用時會有警告,若是是unavilable則會報錯。

在 iOS 和 OS X 中,蘋果也是使用該方法__attribute__進行 API 版本控制,只不過一般使用availability屬性。

帶屬性的枚舉類型(Attributes on Enumerators)

Clang 容許給枚舉值添加屬性來標記某些枚舉項是否可選。如:

enum OperationMode {
  OM_Invalid,
  OM_Normal,
  OM_Terrified __attribute__((deprecated)),
  OM_AbortOnError __attribute__((deprecated)) = 4
};

在 iOS7 SDK 中 UIStatusBarStyle 的定義以下:

typedef NS_ENUM(NSInteger, UIStatusBarStyle) { UIStatusBarStyleDefault = 0, // Dark content, for use on light backgrounds UIStatusBarStyleLightContent NS_ENUM_AVAILABLE_IOS(7_0) = 1, // Light content, for use on dark backgrounds UIStatusBarStyleBlackTranslucent NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 1, UIStatusBarStyleBlackOpaque NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 2, };

其中NS_ENUM_AVAILABLE_IOSNS_ENUM_DEPRECATED_IOS本質上也是使用的__attribute__()

availability 屬性

Clang 提供了availability屬性來描述申明對象的生命週期,如:

void f(void) __attribute__((availability(ios,introduced=4.0,deprecated=6.0,obsoleted=7.0)));

代表函數 f 在 iOS4.0 時被引入,在 6.0 是就不推薦使用,到 iOS7.0 就完全廢棄。

在方法重載的時候,子類的方法能夠比父類的方法申明更大的有效範圍:

@interface A
- (id)method __attribute__((availability(macosx,introduced=10.6)));
- (id)method2 __attribute__((availability(macosx,introduced=10.6)));
@end

@interface B : A
- (id)method __attribute__((availability(macosx,introduced=10.4))); //okay: method moved into base class later
- (id)method2 __attribute__((availability)(macosx,introduced=10.8)); //error: this method was available via the base class in 10.6
@end

Objective-C 相關特性

相關返回類型(related result types)

按照 Cocoa 編碼的慣例,Objective-C 方法以一些關鍵字(如initalloc等)開頭一般會返回一個當前類(Class)的實例對象,這些方法就具備相關返回類型,也就是一個方法返回一個與調用對象同類型的對象。

@interface NSObject
+ (id)alloc;
- (id)init;
@end

@interface NSArray : NSObject
@end

一般的初始化方式爲:

NSArray *array = [[NSArray alloc] init];

表達式[NSArray alloc]返回的是NSArray*類型,由於alloc方法是一個隱性的相關返回類型(related result type)。一樣[[NSArray alloc] init]也是一個NSArray*類型由於init也是一個相關返回類型。若是allocinit都不是相關返回類型,他們返回的就和申明的同樣是id類型。

一個具備相關返回類型的方法可使用instancetype類型做爲返回類型,instancetype是一個上下文相關的關鍵字並只能做爲 Objective-C 方法的返回類型。如:

@interface A
+ (instancetype)constructAnA;
@end

判斷一個方法是不是相關返回類型,能夠考慮方法的第一個單詞(如:initWithObjects中的init),一個相關返回類型方法的返回值的類型和當前類相同,同時:

  • 方法的第一個單詞是allocnew的類方法, 或者
  • 方法的第一個單詞是autoreleaseinitretainself的實例方法。

蘋果本身從 iOS5.0 以後就開始在使用instancetype做爲相關返回類型方法的返回類型。 (更多關於 instancetype 理解可參考:這裏 或 這裏

自動引用計數(ARC)

Clang 提供了對 Objective-C 中自動引用計數的支持,這樣你就不須要手動調用retain/release/autorelease等方法。與自動引用計數相關有兩個宏可用:__has_feature(objc_arc)用來判斷當前環境是不是 ARC,__has_feature(objc_arc_weak)用來判斷weak__weak關鍵字是否可用。

對象字面量和下標

Clang 提供了對 Objective-C 中對象字面量和下標的支持,這些簡化了 Objective-C 編碼方式,讓程序更簡潔。 有幾個宏與此相關:__has_feature(objc_array_literals)判斷數組字面量是否支持,__has_feature(objc_dictionary_literals)判斷字典字面量是否支持,__has_feature(objc_subscripting)判斷對象下標是否支持。

Objective-C 屬性自動合成

Clang 支持聲明的屬性自動合成,也就是隻須要申明屬性@property NSString *name;,就會自動爲 _name 生產 get/set 方法。__has_feature(objc_default_synthesize_properties)能夠檢查屬性自動合成在當前版本的 Clang 下是否支持。

objc_method_family 屬性

在 Objective-C 中方法的命名一般表明着方法類型,如以init開頭的方法會被編譯器默認爲是初始化方法,在編譯的時候會將該方法當成初始化方法對待。可是有時候咱們並不想以編譯器默認的方式給方法取名,或者編譯器默認的方法類型與咱們本身想表示的有出入。咱們就可使用__attribute__((objc_method_family(X)))來明確說明該方法的類型,其中 X 取值爲:none,alloc,copy,init,mutableCopy,new。如:

- (NSString *)initMyStringValue __attribute__((objc_method_family(none)));

這樣該方法在編譯時就不會再被編譯器當作爲初始化方法了。

Objective-C 對象引用屬性

在 Objective-C 中,方法一般是遵循Cocoa Memory Management規範來對參數和返回值進行內存管理的。可是會有特許的狀況,因此 Clang 提供了屬性來標識對象引用狀況。

使用 ns_returns_retained,ns_returns_not_retained,ns_returns_autoreleased,cf_returns_retained,cf_returns_not_retained等屬性來講明方法中對返回值引用的狀況。

id foo() __attribute__((ns_returns_retained));

- (NSString *)bar:(int)x __attribute__((ns_returns_autoreleased));

標記爲*_returns_retained屬性的返回的對象引用計數 +1 了,*_returns_not_retained屬性標記的返回對象引用計數先後沒有改變,*_returns_autorelased屬性標記的返回對象引用計數 +0,可是會在下一個自動釋放池中被釋放掉。

使用 ns_cousumedcf_consumed屬性來標記在方法中會被 +1 的參數,ns_consumes_self屬性在 Objective-C 方法中使用,標記在該方法中 self 對象的引用計數會被 +1。

void foo(__attribute__((ns_consumed) NSString *string);

- (void)bar __attribute__((ns_consumed_self));
- (void)baz:(id) __attribute__((ns_consumed)) x;

你可使用__has_feature()方法判斷這些屬性是否可用。

Posted by TracyYih - 7月 26 2013
如需轉載,請註明: 本文來自 Esoft Mobile

相關文章
相關標籤/搜索