屬性@property中的關鍵字

一. 關于@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分別有什么作用
  1. @property有兩個對應的詞强岸,一個是 @synthesize锻弓,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫蝌箍,那么默認的就是@syntheszie var = _var;
  2. @synthesize 的語義是如果你沒有手動實現 setter 方法和 getter 方法青灼,那么編譯器會自動為你加上這兩個方法。
  3. @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 有什么不同?
  1. 什么情況下使用weak

在ARC中有可能會出現循環(huán)引用的情況,往往通過其中一端使用weak來解決, 比如delagate代理屬性,自身已經對它有過一次強應用,沒有必要再強引用一次枯冈。這個時候也會使用weak毅贮;還有就是自定義IBOutlet控件屬性一般也使用weak,一般情況也可以使用strong尘奏。

  1. 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ā)送消息,很容造成程序奔潰
  1. 不要用assign修飾對象

對象的內存一般被分配到堆上滩褥,基本數據類型和oc數據類型的內存一般被分配在棧上。

用weak修飾, 對象遭到摧毀時,引用計數為0,自動賦值為nil.而使用assign修飾,對象摧毀時,只是引用計數為0, 并不會自動賦值為nil,指針地址還是存在的,之后再向該対像發(fā)消息,就會導致野指針操作.
如果這個操作發(fā)生時內存還沒有改變內容罪既,依舊可以正確的運行铸题,而如果發(fā)生時內存內容被改變了,就會crash琢感。

  1. 總結
  • 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;

兩個問題:

    1. 添加,刪除,修改數組內的元素的時候,程序會因為找不到對應的方法而崩潰.因為 copy 就是復制一個不可變 NSArray 的對象垮衷;
      詳情解釋見 第9題 怎么用 copy 關鍵字
    1. 使用了 atomic 屬性會嚴重影響性能 ;
      該屬性使用了同步鎖乖坠,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序搀突,這會帶來性能問題,通過聲明 nonatomic 可以節(jié)省這些雖然很小但是不必要額外開銷熊泵。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末仰迁,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子顽分,更是在濱河造成了極大的恐慌徐许,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卒蘸,死亡現場離奇詭異绊寻,居然都是意外死亡,警方通過查閱死者的電腦和手機悬秉,發(fā)現死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門澄步,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人和泌,你說我怎么就攤上這事村缸。” “怎么了武氓?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵梯皿,是天一觀的道長。 經常有香客問我县恕,道長东羹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任忠烛,我火速辦了婚禮属提,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己冤议,他們只是感情好斟薇,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恕酸,像睡著了一般堪滨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蕊温,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天袱箱,我揣著相機與錄音,去河邊找鬼义矛。 笑死犯眠,一個胖子當著我的面吹牛,可吹牛的內容都是我干的症革。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼鸯旁,長吁一口氣:“原來是場噩夢啊……” “哼噪矛!你這毒婦竟也來了?” 一聲冷哼從身側響起铺罢,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤艇挨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后韭赘,有當地人在樹林里發(fā)現了一具尸體缩滨,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年泉瞻,在試婚紗的時候發(fā)現自己被綠了脉漏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡袖牙,死狀恐怖侧巨,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情鞭达,我是刑警寧澤司忱,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站畴蹭,受9級特大地震影響坦仍,放射性物質發(fā)生泄漏。R本人自食惡果不足惜叨襟,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一繁扎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧糊闽,春花似錦锻离、人聲如沸铺峭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卫键。三九已至,卻和暖如春虱朵,著一層夾襖步出監(jiān)牢的瞬間莉炉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工碴犬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留絮宁,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓服协,卻偏偏與公主長得像绍昂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子偿荷,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353