本文是自《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 屬性的方法
方法或屬性能夠加上帶deprecated
或unavilable
等屬性來表示狀態。如:
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_IOS
和NS_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 方法以一些關鍵字(如init
,alloc
等)開頭一般會返回一個當前類(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
也是一個相關返回類型。若是alloc
和init
都不是相關返回類型,他們返回的就和申明的同樣是id
類型。
一個具備相關返回類型的方法可使用instancetype
類型做爲返回類型,instancetype
是一個上下文相關的關鍵字並只能做爲 Objective-C 方法的返回類型。如:
@interface A
+ (instancetype)constructAnA;
@end
判斷一個方法是不是相關返回類型,能夠考慮方法的第一個單詞(如:initWithObjects
中的init
),一個相關返回類型方法的返回值的類型和當前類相同,同時:
- 方法的第一個單詞是
alloc
或new
的類方法, 或者 - 方法的第一個單詞是
autorelease
,init
,retain
或self
的實例方法。
蘋果本身從 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_cousumed
和cf_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