OC - 屬性關鍵字和全部權修飾符

網絡配圖

1. 屬性關鍵字有哪些?

分類 屬性關鍵字
原子性 atomic、nonatomic
讀寫權限 readwrite、readonly、setter、getter
內存管理 assign、weak、unsafe_unretained、retain、strong、copy
可空性 (nullable、_Nullable 、__nullable)、
(nonnull、_Nonnull、__nonnull)、
(null_unspecified、_Null_unspecified 、__null_unspecified)、
null_resettable

1.1 原子性

屬性關鍵字 用法
atomic 原子性(默認),編譯器會自動生成互斥鎖,對 setter 和 getter 方法進行加鎖,能夠保證屬性的賦值和取值的原子性操做是線程安全的,但不包括操做和訪問。
好比說 atomic 修飾的是一個數組的話,那麼咱們對數組進行賦值和取值是能夠保證線程安全的。可是若是咱們對數組進行操做,好比說給數組添加對象或者移除對象,是不在 atomic 的負責範圍以內的,因此給被 atomic 修飾的數組添加對象或者移除對象是沒辦法保證線程安全的。
nonatomic 非原子性,通常屬性都用 nonatomic 進行修飾,由於 atomic 很是耗時。

1.2 讀寫權限

屬性關鍵字 用法
readwrite 可讀可寫(默認),同時生成 setter 方法和 getter 方法的聲明和實現。
readonly 只讀,只生成 getter 方法的聲明和實現。
setter 能夠指定生成的 setter 方法名,如 setter = setName。
getter 能夠指定生成的 getter 方法名,如 getter = getName。

1.3 內存管理

屬性關鍵字 用法
assign 1. 既能夠修飾基本數據類型,也能夠修飾對象類型;
2. setter 方法的實現是直接賦值,通常用於基本數據類型 ;
3. 修飾基本數據類型,如 NSInteger、BOOL、int、float 等;
4. 修飾對象類型時,不增長其引用計數;
5. 會產生懸垂指針(懸垂指針:assign 修飾的對象在被釋放以後,指針仍然指向原對象地址,該指針變爲懸垂指針。這時候若是繼續經過該指針訪問原對象的話,就可能致使程序崩潰)。
weak 1. 只能修飾對象類型;
2. ARC 下才能使用;
3. 修飾弱引用,不增長對象引用計數,主要能夠用於避免循環引用;
4. weak 修飾的對象在被釋放以後,會自動將指針置爲 nil,不會產生懸垂指針。
unsafe_unretained 1. 既能夠修飾基本數據類型,也能夠修飾對象類型;
2. MRC 下常用,ARC 下基本不用;
3. 同 weak,區別就在於 unsafe_unretained 會產生懸垂指針。
retain 1. MRC 下使用,ARC 下基本使用 strong;
2. 修飾強引用,將指針原來指向的舊對象釋放掉,而後指向新對象,同時將新對象的引用計數加1;
3. setter 方法的實現是 release 舊值,retain 新值,用於OC對象類型。
strong 1. ARC 下才能使用;
2. 原理同 retain;
3. 可是在修飾 block 時,strong 至關於 copy,而 retain 至關於 assign。
copy setter 方法的實現是 release 舊值,copy 新值,用於 NSString、block 等類型。

1.4 可空性

Apple Blog:Nullability and Objective-C程序員

蘋果在 Xcode 6.3 引入的一個 Objective-C 的新特性nullability annotations。這些關鍵字能夠用於屬性、方法返回值和參數中,來指定對象的可空性,這樣編寫代碼的時候就會智能提示。在 Swift 中可使用?!來表示一個對象是optional的仍是non-optional,如UIView?UIView!。而在 Objective-C 中則沒有這一區分,UIView便可表示這個對象是optional,也可表示是non-optioanl。這樣就會形成一個問題:在 Swift 與 Objective-C 混編時,Swift 編譯器並不知道一個 Objective-C 對象究竟是optional仍是non-optional,所以這種狀況下編譯器會隱式地將 Objective-C 的對象當成是non-optional。引入nullability annotations一方面爲了讓 iOS 程序員平滑地從 Objective-C 過渡到 Swift,另外一方面也促使開發者在編寫 Objective-C 代碼時更加規範,減小同事之間的溝通成本。面試

關鍵字__nullable__nonnull是蘋果在 Xcode 6.3 中發行的。因爲與第三方庫的潛在衝突,蘋果在 Xcode 7 中將它們更改成_Nullable_Nonnull。可是,爲了與 Xcode 6.3 兼容,蘋果預約義了宏__nullable__nonnull來擴展爲新名稱。同時蘋果一樣還支持沒有下劃線的寫法nullablenonnull,它們的區別在與放置位置不一樣。swift

注意:此類關鍵詞僅僅提供警告,並不會報編譯錯誤。只能用於聲明對象類型,不能聲明基本數據類型。數組

屬性關鍵字 用法
nullable、_Nullable 、__nullable 對象能夠爲空,區別在於放置位置不一樣
nonnull、_Nonnull、__nonnull 對象不能爲空,區別在於放置位置不一樣
null_unspecified、_Null_unspecified 、__null_unspecified 未指定是否可爲空,區別在於放置位置不一樣
null_resettable 1. getter 方法不能返回爲空,setter 方法能夠爲空;
2. 必須重寫 setter 或 getter 方法作非空處理。不然會報警告Synthesized setter 'setName:' for null_resettable property 'name' does not handle nil

使用示例

  • 聲明屬性
@property (nonatomic, copy, nullable) NSString * param;
@property (nonatomic, copy) NSString * _Nullable param;
@property (nonatomic, copy) NSString * __nullable param;
複製代碼
  • 修飾方法返回值
- (nullable NSString *)method;
- (NSString * _Nullable)method;
- (NSString * __nullable)method;
複製代碼
  • 修飾方法參數
- (void)methodWithParam:(nullable NSString *) param;
- (void)methodWithParam:(NSString * _Nullable) param;
- (void)methodWithParam:(NSString * __nullable) param;
複製代碼
  • 例外狀況:對於雙指針類型對象Block 的返回值Block 的參數等,這時候就不能用nonnull/nullable修飾,只能用帶下劃線的__nonnull/__nullable或者 _Nonnull/_Nullable
- (void)methodWithError:(NSError * _Nullable * _Nullable)error
- (void)methodWithError:(NSError * __nullable * __nullable)error;
複製代碼
- (void)methodWithBlock:(nullable id __nonnull  (^)(id __nullable params))block;
複製代碼

使用效果

@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (AAPLListItem * _Nullable)itemWithName:(NSString * _Nonnull)name;
@property (copy, readonly) NSArray * _Nonnull allItems;
// ...
@end

// --------------

[self.list itemWithName:nil]; // warning!
複製代碼

Audited Regions:Nonnull 區域設置

若是每一個屬性或每一個方法都去指定nonnullnullable,將是一件很是繁瑣的事。蘋果爲了減輕咱們的工做量,專門提供了兩個宏:NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END。在這兩個宏之間的代碼,全部簡單指針類型都被假定爲nonnull,所以咱們只須要去指定那些nullable指針類型便可。示例代碼以下:安全

NS_ASSUME_NONNULL_BEGIN
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (nullable AAPLListItem *)itemWithName:(NSString *)name;
- (NSInteger)indexOfItem:(AAPLListItem *)item;

@property (copy, nullable) NSString *name;
@property (copy, readonly) NSArray *allItems;
// ...
@end
NS_ASSUME_NONNULL_END

// --------------

self.list.name = nil;   // okay

AAPLListItem *matchingItem = [self.list itemWithName:nil];  // warning!
複製代碼

使用規範:

  • 對於屬性、方法返回值、方法參數的修飾,使用:nonnull/nullable
  • 對於 C 函數的參數、Block 的參數、Block 返回值的修飾,使用:_Nonnull/_Nullable,建議棄用__nonnull/__nullable

爲了安全起見,蘋果還制定瞭如下幾條規則:網絡

  • typedef類型的的可空性一般依賴於上下文,即便在 Audited Regions 中也不能假定它爲nonnull
  • 對於複雜的指針類型(如id *)必須明確指定它的可空性。例如,指定一個指向nullable對象的nonnull指針,可使用_Nullable id * _Nonnull
  • 特殊類型的NSError **常常用於經過方法參數返回錯誤,所以始終假定它是指向nullableNSError對象的nullable的指針。

2.全部權修飾符

全部權修飾符 用法
__strong 1. 強引用持有對象,能夠對應 strong、retain、copy 關鍵字。
2. 編譯器將爲 strong、retain、copy 修飾的屬性生成帶 __strong 全部權修飾符的實例變量。
__weak 1. 弱引用持有對象,對應 weak 關鍵字,ARC下用來防止循環引用。
2. 編譯器將爲 weak 修飾的屬性生成帶 __weak 全部權修飾符的實例變量。
__unsafe_unretained 1. 弱引用持有對象,對應 unsafe_unretained、assign 關鍵字,MRC下用來防止循環引用。
2. 編譯器將爲 unsafe_unretained 修飾的屬性生成帶 __unsafe_unretained 全部權修飾符的實例變量。
3. 與 __weak 相比,它不須要遍歷 weak 表來檢查對象是否 nil,性能上要更好一些。可是它會產生懸垂指針。
__autoreleasing 在 MRC 中咱們能夠給對象發生 autorelease 消息來將它註冊到 autoreleasepool 中,而在 ARC 中咱們可使用 __autoreleasing 修飾符修飾對象將對象註冊到 autoreleasepool 中。

關於全部權修飾符的詳細解釋,能夠參閱《iOS - 老生常談內存管理(三):ARC 面世》app

3.相關面試題

Q:atomic 修飾的屬性是怎麼樣保存線程安全的?

答: 編譯器會自動生成互斥鎖,對 setter 和 getter 方法進行加鎖,能夠保證屬性的賦值和取值原子性操做是線程安全的,但不包括操做和訪問。
好比說atomic修飾的是一個數組的話,那麼咱們對數組進行賦值和取值是能夠保證線程安全的。可是若是咱們對數組進行操做,好比說給數組添加對象或者移除對象,是不在atomic的負責範圍以內的,因此給被atomic修飾的數組添加對象或者移除對象是沒辦法保證線程安全的。函數

Q:何時使用 weak/__weak 關鍵字?

  • ① ARC 中爲了不循環引用而使用,可讓相互引用的對象中的一個使用weak/__weak弱引用修飾,經常使用於對delegateblock的修飾;
  • ② Interface Builder 中 IBOutlet 修飾的控件通常也是用weak

Q:assign 和 weak 關鍵字的區別有哪些?

  • weak只能修飾對象,而assign既能夠修飾對象也能夠修飾基本數據類型;
  • assign修飾的對象在被釋放後,指針仍然指向原對象地址;而weak修飾的對象在被釋放以後會自動置指針爲 nil;
  • ③ 相同點:在修飾對象的時候,assignweak都不改變對象的引用計數。

Q:如下代碼會出現什麼問題?(深淺拷貝)

@property (copy) NSMutableArray *array;
複製代碼

答: 不論賦值過來的是NSMutableArray仍是NSArray對象,進行copy操做後都是NSArray對象(深拷貝)。因爲屬性被聲明爲NSMutableArray類型,就不可避免的會有調用方去調用它的添加對象、移除對象等一些方法,此時因爲copy的結果是NSArray不可變對象,對NSArray對象調用添加對象、移除對象等方法,就會產生程序異常。post

參考

Nullability and Objective-C(Apple Blog)性能

相關文章
相關標籤/搜索