一. 關于@property
- @property, 是聲明屬性的語法,在iOS日常開發(fā)中經常會使用。
- 其實就是由編譯器自動幫我們生成ivar成員變量,getter方法讨衣,setter方法方援。
二. @property屬性關鍵字
我們經常使用assign,weak,strong,copy,nonatomic,atomic,readonly,readwrite,getter,setter等關鍵字, 他們具體作用是什么没炒。
關鍵字 | 關鍵字作用 |
---|---|
nonatomic | 非原子性操作,不提供線程安全,多線程并發(fā)訪問會提高性能犯戏。 |
atomic | 原子操作,提供線程安全,默認是atomic,耗費系統(tǒng)資源 |
readwrite | 讀寫的送火,默認屬性 |
readonly | 只可以讀,不能寫先匪,可以獲取 |
writeonly | 只能寫(set)种吸,不能讀(get), 一般用不到 |
assign | 不會使引用計數加1,直接賦值,適用基礎數據類型(int float double等) |
retain | 會使引用計數加1,ARC下已經不再使用,用strong代替呀非。 |
copy | 建立一個索引計數為1的對象坚俗,在賦值時使用傳入值的一份拷貝,適用于NSString和block |
strong | 會使引用計數加1, ARC時才會使用镜盯,相當于retain。 |
weak | 不增加引用計數,也不持有對象,ARC時才會使用猖败,對象消失可以把對應的指針變量置為nil |
unsafe_unretained | 和weak類似速缆,但是引用計數為0,變量不會置為nil |
getter | 手動設置獲取實例變量的方法 |
setter | 手動設置設置實例變量的方法 |
還有不常用關鍵字 nonnull,null_resettable,nullable
getter和setter關鍵字的解釋
通過設置setter和getter關鍵字來修改setter和getter方法的方法名恩闻。
@property (getter=getName, setter=setName)object *obj;
//這樣修飾就不會執(zhí)行系統(tǒng)的getter和setter方法了艺糜,會執(zhí)行自定義的getName和setName方法。
setter=<name>和getter=<name>一般用在特殊的情境下,當需要定義一個 init 開頭的屬性判呕,但默認生成的 setter 與 getter 方法也會以 init 開頭倦踢,而編譯器會把所有以 init 開頭的方法當成初始化方法,而初始化方法只能返回 self 類型侠草,因此編譯器會報錯辱挥。
這時你就可以使用下面的方式來避免編譯器報錯:
@property(nonatomic, strong, getter=p_initBy, setter=setP_initBy:)NSString *initBy;
另外也可以用關鍵字進行特殊說明,來避免編譯器報錯:
@property(nonatomic, readwrite, copy, null_resettable) NSString *initBy;
- (NSString *)initBy __attribute__((objc_method_family(none)));
三. 自動合成(auto synthesize)
自動合成(auto synthesize)
這個過程是由編譯器在編輯階段執(zhí)行, 編譯器自動向類中添加成員變量(在屬性名前面加下劃線)边涕、生成setter晤碘、getter方法,在編譯器里看不到這些"合成方法"源碼。
但自動合成總有例外
- 在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,
如果在協(xié)議中定義了是屬性功蜓,就必須在實現類中用@synthesize添加對屬性自動同步或者手動添加屬性的成員變量及方法實現代碼
// Protocol
@protocol MyProtocol <NSObject>
@property (nonatomic,strong) NSString *myImage;
@end
// 實現類
@interface ViewController : UIViewController<MyProtocol>
@end
@implementation ViewController
// 添加對屬性自動同步
@synthesize myName = _myName;
- (void)viewDidLoad {
[super viewDidLoad];
self.myName = @"name";
NSLog(@"%@,%@",_myName,self.myName);
@end
- 在category中可以用@property來添加屬性园爷,此種方式會自動生成對應屬性的set和get方法的聲明,但是沒有set和get方法的實現式撼,也不會自動生成帶有“_”的屬性(編譯會通過童社,但run之后就會崩潰),但category中不支持用@synthesize添加對屬性自動同步,但我們可以通過runtime手動添加setter/getter方法。
// 在category的聲明中添加name屬性:
#import <UIKit/UIKit.h>
@interface UIView (ETName)
@property(nonatomic,copy) NSString *name;
@end
//在category的實現中通過運行時重寫屬性的set和get著隆,必須引入runtime.h頭文件扰楼。
#import "UIView+ETName.h"
#import <objc/runtime.h>
@implementation UIView (ETName)
-(void)setName:(NSString *)name
{
//self表示正在運行的對象,“NAME”是C的標識美浦,name為添加的新屬性的值弦赖,最后一個參數是屬性修飾符(枚舉)
objc_setAssociatedObject(self, "NAME", name, OBJC_ASSOCIATION_COPY_NONATOMIC );
}
-(NSString *)name
{
return objc_getAssociatedObject(self, "NAME");
}
@end
四. @synthesize 和 @dynamic
- @property 有兩個對應的詞,@synthesize和@dynamic
- @synthesize 的語義是如果你沒有手動實現 setter 方法和 getter 方法,那么編譯器會自動為你加上這兩個方法浦辨。
- @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現蹬竖,不自動生成。
- 如果 @synthesize和 @dynamic都沒有流酬,默認自動合成
四. 以下是關于@property屬性相關面試題
1. 使用 atomic 一定是線程安全的嗎?
答案很明顯币厕。不是,
- atomic 的本意是指屬性的存取方法是線程安全的,并不保證整個對象是線程安全的。
例如:
聲明一個 NSMutableArray 的原子屬性 stuff,此時 self.stuff 和 self.stuff =othersulf 都是線程安全的芽腾。但是,使用[self.stuff objectAtIndex:index]就不是線程安全的,需要用互斥鎖來保證線程安全性旦装。
2. @property 的本質是什么?ivar晦嵌、getter、setter 是如何生成并添加到這個類中的
@property 的本質是什么?
- @property = ivar + getter + setter;
- “屬性” (property)有兩大概念:ivar(實例變量)惭载、存取方法(access method = getter + setter)
ivar旱函、getter、setter 是如何生成并添加到這個類中的?
“自動合成”( autosynthesis)
完成屬性定義后描滔,編譯器會自動編寫訪問這些屬性所需的方法棒妨,此過程叫做“自動合成”(autosynthesis)。需要強調的是含长,這個過程由編譯 器在編譯期執(zhí)行券腔,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter拘泞、setter 之外纷纫,編譯器還要自動向類中添加適當類型的實例變量,并且在屬性名前面加下劃線陪腌,以此作為實例變量的名字辱魁。例如: 屬foo,會生成實例變量 _foo诗鸭。也可以在類的實現代碼里通過 @synthesize 語法來指定實例變量的名字.
3. @protocol 和 category 中如何使用 @property
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,可以在實現類中用@synthesize添加對屬性自動同步或者手動添加屬性的成員變量及方法實現代碼
category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實現,需要借助于運行時的兩個函數:
1染簇、objc_setAssociatedObject
2、objc_getAssociatedObject
具體實現見上文"自動合成"
4. @synthesize和@dynamic分別有什么作用
- @property有兩個對應的詞强岸,一個是 @synthesize锻弓,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫蝌箍,那么默認的就是@syntheszie var = _var;
- @synthesize 的語義是如果你沒有手動實現 setter 方法和 getter 方法青灼,那么編譯器會自動為你加上這兩個方法。
- @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現十绑,不自動生成聚至。(當然對于 readonly 的屬性只需提供 getter 即可)。假如一個屬性被聲明為 @dynamic var本橙,然后你沒有提供 @setter方法和 @getter 方法扳躬,編譯的時候沒問題,但是當程序運行到 instance.var = someVar甚亭,由于缺 setter 方法會導致程序崩潰贷币;或者當運行到 someVar = var時,由于缺 getter 方法同樣會導致崩潰亏狰。編譯時沒問題役纹,運行時才執(zhí)行相應的方法,這就是所謂的動態(tài)綁定暇唾。
5.在有了自動合成屬性實例變量之后促脉,@synthesize還有哪些使用場景辰斋?
- 同時復寫了setter和getter方法
- 復寫了只讀屬性的getter方法
- 使用了@dynamic
- Protocol里聲明的所有屬性
- Category里聲明的所有屬性
- 重載的屬性
當你在子類中重載了父類的屬性必須使用@synthesize手動實現ivar - 通過 @synthesize 語法來指定實例變量的名字
6. @synthesize合成實例變量的規(guī)則是什么?假如property名為foo瘸味,存在一個名為_foo的實例變量宫仗,那么還會自動合成新變量么?
合成實例變量規(guī)則
如果指定了成員變量的名稱,會生成一個指定的名稱的成員變量,
如果這個成員已經存在了就不再生成了.
如果是 @synthesize foo; 還會生成一個名稱為foo的成員變量旁仿,也就是說:
如果沒有指定成員變量的名稱會自動生成一個屬性同名的成員變量,如果是 @synthesize foo = _foo; 就不會生成成員變量了.
假如 property 名為 foo藕夫,存在一個名為 _foo 的實例變量,那么還會自動合成新變量么
不會
@property(nonatomic, copy) NSString *name;
/*
下面一行代碼會報出警告
Auto property synthesis will not synthesize property "_name" because it cannot
share an ivar with another synthesized property
*/
@property(nonatomic,copy) NSString *_name;
7. ARC 下,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些?
- 基本數據: atomic,readwrite,assign
- 普通的 OC 對象: atomic,readwrite,strong
8. 什么情況使用 weak 關鍵字,相比 assign 有什么不同?
- 什么情況下使用weak
在ARC中有可能會出現循環(huán)引用的情況,往往通過其中一端使用weak來解決, 比如delagate代理屬性,自身已經對它有過一次強應用,沒有必要再強引用一次枯冈。這個時候也會使用weak毅贮;還有就是自定義IBOutlet控件屬性一般也使用weak,一般情況也可以使用strong尘奏。
- weak和assign的區(qū)別
- assign可以用于非OC對象, 可以修飾OC數據類型, 和基本數據類型.(OC: CGFloat, NSInteger等, 非OC: int, float等)
- weak必須用于OC對象.
- assign修飾對象會產生野指針, weak不會
weak 策略在屬性所指的對象遭到摧毀時,系統(tǒng)會將 weak 修飾的屬性對象的指針指向 nil,在 OC 給 nil 發(fā)消息是不會有什么問題的;如果使用 assign 策略在屬性所指的對象遭到摧毀時,屬性對象指針還指向原來的對象,由于對象已經被銷毀,這時候就產生了野指針,如果這時候在給此對象發(fā)送消息,很容造成程序奔潰
- 不要用assign修飾對象
對象的內存一般被分配到堆上滩褥,基本數據類型和oc數據類型的內存一般被分配在棧上。
用weak修飾, 對象遭到摧毀時,引用計數為0,自動賦值為nil.而使用assign修飾,對象摧毀時,只是引用計數為0, 并不會自動賦值為nil,指針地址還是存在的,之后再向該対像發(fā)消息,就會導致野指針操作.
如果這個操作發(fā)生時內存還沒有改變內容罪既,依舊可以正確的運行铸题,而如果發(fā)生時內存內容被改變了,就會crash琢感。
- 總結
-
weak
表明該屬性定義了一種(nonowning relationship)非擁有關系.為這種屬性賦值時, 既不會保留新值,也不釋放舊值. - 在ARC模式下編程時丢间,指針變量一定要用weak修飾,例如delegate和block一定要用weak修飾驹针。不會導致野指針問題烘挫,也不會循環(huán)引用
- 只有基本數據類型和結構體需要用assgin,因為值類型會被放入棧中柬甥,遵循先進后出原則饮六,由系統(tǒng)負責管理棧內存。而引用類型會被放入堆中苛蒲,需要我們自己手動管理內存或通過ARC管理卤橄。
9. runtime 如何實現 weak 屬性
weak 屬性的特點:
weak
此特質表明該屬性定義了一種“非擁有關系” (nonowning relationship)。為這種屬性設置新值時臂外,設置方法既不保留新值窟扑,也不釋放舊值。此特質同 assign 類似漏健, 然而在屬性所指的對象遭到摧毀時嚎货,屬性值也會清空(nil out)。runtime 如何實現 weak 變量的自動置nil蔫浆?
runtime
對注冊的類殖属, 會進行布局,對于 weak 對象會放入一個hash
表中瓦盛。 用 weak 指向的對象內存地址作為 key洗显,當此對象的引用計數為0的時候會 dealloc外潜,假如 weak 指向的對象內存地址是a,那么就會以a為鍵挠唆, 在這個 weak 表中搜索橡卤,找到所有以a為鍵的 weak 對象,從而設置為 nil损搬。如何讓不使用weak修飾的@property,擁有weak的效果柜与。
(在一些博客中看到利用runtime實現了此效果, 以下是參考源碼)
源碼: CYLDeallocBlockExecutor
10. IBOutlet連出來的視圖屬性為什么可以被設置成weak
參考鏈接: Should IBOutlets be strong or weak under ARC?
文章告訴我們:
因為既然有外鏈那么視圖在xib或者storyboard中肯定存在巧勤,視圖已經對它有一個強引用了。
不過這個回答漏了個重要知識弄匕,使用storyboard(xib不行)創(chuàng)建的vc颅悉,會有一個叫_topLevelObjectsToKeepAliveFromStoryboard的私有數組強引用所有top level的對象,所以這時即便outlet聲明成weak也沒關系
11. 怎么用 copy 關鍵字迁匠?
NSString剩瓶、NSArray、NSDictionary 等等經常使用copy關鍵字城丧,是因為他們有對應的可變類型:NSMutableString延曙、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作亡哄,為確保對象中的字符串值不會無意間變動枝缔,應該在設置新屬性值時拷貝一份。
解釋copy 此特質所表達的所屬關系與 strong 類似蚊惯。然而設置方法并不保留新值愿卸,而是將其“拷貝” (copy)。 當屬性類型為 NSString 時截型,經常用此特質來保護其封裝性趴荸,因為傳遞給設置方法的新值有可能指向一個 NSMutableString 類的實例。這個類是 NSString 的子類宦焦,表示一種可修改其值的字符串发钝,此時若是不拷貝字符串,那么設置完屬性之后赶诊,字符串的值就可能會在對象不知情的情況下遭人更改笼平。所以,這時就要拷貝一份“不可變” (immutable)的字符串舔痪,確保對象中的字符串值不會無意間變動寓调。只要實現屬性所用的對象是“可變的” (mutable),就應該在設置新屬性值時拷貝一份锄码。
block 也經常使用 copy 關鍵字夺英,
解釋block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的晌涕,如果不寫 copy ,該類的調用者有可能會忘記或者根本不知道“編譯器會自動對 block 進行了 copy 操作”痛悯,他們有可能會在調用之前自行拷貝屬性值余黎。
12. 用@property聲明的NSString(或NSArray,NSDictionary)經常使用copy關鍵字载萌,為什么惧财?如果改用strong關鍵字,可能造成什么問題扭仁?
因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
詳情解釋見 第9題 怎么用 copy 關鍵字
如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
13. 這個寫法會出什么問題: @property (copy) NSMutableArray *array;
兩個問題:
- 添加,刪除,修改數組內的元素的時候,程序會因為找不到對應的方法而崩潰.因為 copy 就是復制一個不可變 NSArray 的對象垮衷;
詳情解釋見 第9題 怎么用 copy 關鍵字
- 添加,刪除,修改數組內的元素的時候,程序會因為找不到對應的方法而崩潰.因為 copy 就是復制一個不可變 NSArray 的對象垮衷;
- 使用了 atomic 屬性會嚴重影響性能 ;
該屬性使用了同步鎖乖坠,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序搀突,這會帶來性能問題,通過聲明 nonatomic 可以節(jié)省這些雖然很小但是不必要額外開銷熊泵。
- 使用了 atomic 屬性會嚴重影響性能 ;