《招聘一個靠譜的 iOS》—參考答案(一)

《招聘一個靠譜的 iOS》—參考答案(上)

說明:面試題來源是微博@我就叫Sunny怎么了的這篇博文:《招聘一個靠譜的 iOS》屋厘,其中共55題蛆挫,除第一題為糾錯題外,其他54道均為簡答題。

出題者簡介: 孫源(sunnyxx)踱讨,目前就職于百度,負(fù)責(zé)百度知道 iOS 客戶端的開發(fā)工作砍的,對技術(shù)喜歡刨根問底和總結(jié)最佳實(shí)踐痹筛,熱愛分享和開源,維護(hù)一個叫 forkingdog 的開源小組廓鞠。

答案為微博@iOS程序犭袁整理帚稠,未經(jīng)出題者校對,如有紕漏床佳,請向微博@iOS程序犭袁指正滋早。


索引

  1. 風(fēng)格糾錯題

  2. 優(yōu)化部分

  3. 硬傷部分

  4. 什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同砌们?

  5. 怎么用 copy 關(guān)鍵字杆麸?

  6. 這個寫法會出什么問題: @property (copy) NSMutableArray *array;

  7. 如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關(guān)鍵字的 setter浪感?

  8. @property 的本質(zhì)是什么昔头?ivar、getter影兽、setter 是如何生成并添加到這個類中的

  9. @protocol 和 category 中如何使用 @property

  10. runtime 如何實(shí)現(xiàn) weak 屬性

  11. @property中有哪些屬性關(guān)鍵字揭斧?/ @property 后面可以有哪些修飾符?

  12. weak屬性需要在dealloc中置nil么峻堰?

  13. @synthesize和@dynamic分別有什么作用讹开?

  14. ARC下,不顯式指定任何屬性關(guān)鍵字時茧妒,默認(rèn)的關(guān)鍵字都有哪些萧吠?

  15. 用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字桐筏,為什么纸型?如果改用strong關(guān)鍵字,可能造成什么問題梅忌?

  16. 對非集合類對象的copy操作

  17. 集合類對象的copy與mutableCopy

  18. @synthesize合成實(shí)例變量的規(guī)則是什么狰腌?假如property名為foo,存在一個名為_foo的實(shí)例變量牧氮,那么還會自動合成新變量么琼腔?

  19. 在有了自動合成屬性實(shí)例變量之后,@synthesize還有哪些使用場景踱葛?

  20. objc中向一個nil對象發(fā)送消息將會發(fā)生什么丹莲?

  21. objc中向一個對象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系光坝?

  22. 什么時候會報(bào)unrecognized selector的異常?

  23. 一個objc對象如何進(jìn)行內(nèi)存布局甥材?(考慮有父類的情況)

  24. 一個objc對象的isa的指針指向什么盯另?有什么作用?

  25. 下面的代碼輸出什么洲赵?

   @implementation Son : Father
   - (id)init
   {
       self = [super init];
       if (self) {
           NSLog(@"%@", NSStringFromClass([self class]));
           NSLog(@"%@", NSStringFromClass([super class]));
       }
       return self;
   }
   @end
  1. 22--55題鸳惯,請看下篇。

1. 風(fēng)格糾錯題

enter image description here
enter image description here

修改完的代碼:

修改方法有很多種叠萍,現(xiàn)給出一種做示例:

// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 修改完的代碼芝发,這是第一種修改方法,后面會給出第二種修改方法

typedef NS_ENUM(NSInteger, CYLSex) {
   CYLSexMan,
   CYLSexWoman
};

@interface CYLUser : NSObject<NSCopying>

@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) CYLSex sex;

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;

@end

下面對具體修改的地方苛谷,分兩部分做下介紹:硬傷部分優(yōu)化部分
辅鲸。因?yàn)?strong>硬傷部分沒什么技術(shù)含量,為了節(jié)省大家時間抄腔,放在后面講瓢湃,大神請直接看優(yōu)化部分

優(yōu)化部分

  1. enum 建議使用 NS_ENUMNS_OPTIONS 宏來定義枚舉類型赫蛇,參見官方的 Adopting Modern Objective-C 一文:

//定義一個枚舉
typedef NS_ENUM(NSInteger, CYLSex) {
CYLSexMan,
CYLSexWoman
};

 (僅僅讓性別包含男和女可能并不嚴(yán)謹(jǐn)绵患,最嚴(yán)謹(jǐn)?shù)淖龇梢詤⒖?[這里](https://github.com/ChenYilong/iOSInterviewQuestions/issues/9) 。)
 2. age 屬性的類型:應(yīng)避免使用基本類型悟耘,建議使用 Foundation 數(shù)據(jù)類型落蝙,對應(yīng)關(guān)系如下:
 
 ```Objective-C
    int -> NSInteger
    unsigned -> NSUInteger
    float -> CGFloat
    動畫時間 -> NSTimeInterval

同時考慮到 age 的特點(diǎn),應(yīng)使用 NSUInteger 暂幼,而非 int 筏勒。
這樣做的是基于64-bit 適配考慮,詳情可參考出題者的博文《64-bit Tips》旺嬉。

  1. 如果工程項(xiàng)目非常龐大管行,需要拆分成不同的模塊,可以在類邪媳、typedef宏命名的時候使用前綴捐顷。
  2. doLogIn方法不應(yīng)寫在該類中: <p><del>雖然LogIn的命名不太清晰,但筆者猜測是login的意思雨效, (勘誤:Login是名詞迅涮,LogIn 是動詞,都表示登陸的意思徽龟。見: Log in vs. login )</del></p>登錄操作屬于業(yè)務(wù)邏輯叮姑,觀察類名 UserModel ,以及屬性的命名方式据悔,該類應(yīng)該是一個 Model 而不是一個“ MVVM 模式下的 ViewModel ”:

無論是 MVC 模式還是 MVVM 模式传透,業(yè)務(wù)邏輯都不應(yīng)當(dāng)寫在 Model 里:MVC 應(yīng)在 C耘沼,MVVM 應(yīng)在 VM。

(如果拋開命名規(guī)范旷祸,假設(shè)該類真的是 MVVM 模式里的 ViewModel 耕拷,那么 UserModel 這個類可能對應(yīng)的是用戶注冊頁面,如果有特殊的業(yè)務(wù)需求托享,比如: -logIn 對應(yīng)的應(yīng)當(dāng)是注冊并登錄的一個 Button ,出現(xiàn) -logIn 方法也可能是合理的浸赫。)

  1. doLogIn 方法命名不規(guī)范:添加了多余的動詞前綴闰围。
    請牢記:

如果方法表示讓對象執(zhí)行一個動作,使用動詞打頭來命名既峡,注意不要使用 do羡榴,does 這種多余的關(guān)鍵字,動詞本身的暗示就足夠了运敢。

應(yīng)為 -logIn (注意: Login 是名詞, LogIn 是動詞,都表示登陸表伦。 見 Log in vs. login

  1. -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中不要用 with 來連接兩個參數(shù): withAge: 應(yīng)當(dāng)換為age:体啰,age: 已經(jīng)足以清晰說明參數(shù)的作用,也不建議用 andAge: :通常情況下卦方,即使有類似 withA:withB: 的命名需求羊瘩,也通常是使用withA:andB: 這種命名,用來表示方法執(zhí)行了兩個相對獨(dú)立的操作(從設(shè)計(jì)上來說盼砍,這時候也可以拆分成兩個獨(dú)立的方法)尘吗,它不應(yīng)該用作闡明有多個參數(shù),比如下面的:
//錯誤浇坐,不要使用"and"來連接參數(shù)
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
//錯誤睬捶,不要使用"and"來闡明有多個參數(shù)
- (instancetype)initWithName:(CGFloat)width andAge:(CGFloat)height;
//正確,使用"and"來表示兩個相對獨(dú)立的操作
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
  1. 由于字符串值可能會改變近刘,所以要把相關(guān)屬性的“內(nèi)存管理語義”聲明為 copy 擒贸。(原因在下文有詳細(xì)論述:用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字跌宛,為什么酗宋?)
  2. “性別”(sex)屬性的:該類中只給出了一種“初始化方法” (initializer)用于設(shè)置“姓名”(Name)和“年齡”(Age)的初始值,那如何對“性別”(Sex)初始化疆拘?

Objective-C 有 designated 和 secondary 初始化方法的觀念蜕猫。 designated 初始化方法是提供所有的參數(shù),secondary 初始化方法是一個或多個哎迄,并且提供一個或者更多的默認(rèn)參數(shù)來調(diào)用 designated 初始化方法的初始化方法回右。舉例說明:


   // .m文件
   // http://weibo.com/luohanchenyilong/
   // https://github.com/ChenYilong
   //

   @implementation CYLUser

   - (instancetype)initWithName:(NSString *)name
                            age:(NSUInteger)age
                            sex:(CYLSex)sex {
       if(self = [super init]) {
           _name = [name copy];
           _age = age;
           _sex = sex;
       }
       return self;
   }

   - (instancetype)initWithName:(NSString *)name
                            age:(NSUInteger)age {
       return [self initWithName:name age:age sex:nil];
   }

   @end

上面的代碼中initWithName:age:sex: 就是 designated 初始化方法隆圆,另外的是 secondary 初始化方法。因?yàn)閮H僅是調(diào)用類實(shí)現(xiàn)的 designated 初始化方法翔烁。

因?yàn)槌鲱}者沒有給出 .m 文件渺氧,所以有兩種猜測:1:本來打算只設(shè)計(jì)一個 designated 初始化方法,但漏掉了“性別”(sex)屬性蹬屹。那么最終的修改代碼就是上文給出的第一種修改方法侣背。2:不打算初始時初始化“性別”(sex)屬性,打算后期再修改慨默,如果是這種情況贩耐,那么應(yīng)該把“性別”(sex)屬性設(shè)為 readwrite 屬性,最終給出的修改代碼應(yīng)該是:



   // .h文件
   // http://weibo.com/luohanchenyilong/
   // https://github.com/ChenYilong
   // 第二種修改方法(基于第一種修改方法的基礎(chǔ)上)

   typedef NS_ENUM(NSInteger, CYLSex) {
       CYLSexMan,
       CYLSexWoman
   };

   @interface CYLUser : NSObject<NSCopying>

   @property (nonatomic, readonly, copy) NSString *name;
   @property (nonatomic, readonly, assign) NSUInteger age;
   @property (nonatomic, readwrite, assign) CYLSex sex;

   - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
   - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
   + (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;

   @end

.h 中暴露 designated 初始化方法厦取,是為了方便子類化 (想了解更多潮太,請戳--》 《禪與 Objective-C 編程藝術(shù) (Zen and the Art of the Objective-C Craftsmanship 中文翻譯)》。)

  1. 按照接口設(shè)計(jì)的慣例虾攻,如果設(shè)計(jì)了“初始化方法” (initializer)铡买,也應(yīng)當(dāng)搭配一個快捷構(gòu)造方法。而快捷構(gòu)造方法的返回值霎箍,建議為 instancetype奇钞,為保持一致性,init 方法和快捷構(gòu)造方法的返回類型最好都用 instancetype朋沮。
  2. 如果基于第一種修改方法:既然該類中已經(jīng)有一個“初始化方法” (initializer)蛇券,用于設(shè)置“姓名”(Name)、“年齡”(Age)和“性別”(Sex)的初始值:
    那么在設(shè)計(jì)對應(yīng) @property 時就應(yīng)該盡量使用不可變的對象:其三個屬性都應(yīng)該設(shè)為“只讀”樊拓。用初始化方法設(shè)置好屬性值之后纠亚,就不能再改變了。在本例中筋夏,仍需聲明屬性的“內(nèi)存管理語義”蒂胞。于是可以把屬性的定義改成這樣
       @property (nonatomic, readonly, copy) NSString *name;
       @property (nonatomic, readonly, assign) NSUInteger age;
       @property (nonatomic, readonly, assign) CYLSex sex;
  由于是只讀屬性,所以編譯器不會為其創(chuàng)建對應(yīng)的“設(shè)置方法”条篷,即便如此骗随,我們還是要寫上這些屬性的語義,以此表明初始化方法在設(shè)置這些屬性值時所用的方式赴叹。要是不寫明語義的話鸿染,該類的調(diào)用者就不知道初始化方法里會拷貝這些屬性,他們有可能會在調(diào)用初始化方法之前自行拷貝屬性值乞巧。這種操作多余而且低效涨椒。
  1. initUserModelWithUserName 如果改為 initWithName 會更加簡潔,而且足夠清晰。
  2. UserModel 如果改為 User 會更加簡潔蚕冬,而且足夠清晰免猾。
  3. UserSex如果改為Sex 會更加簡潔,而且足夠清晰囤热。
  4. 第二個 @property 中 assign 和 nonatomic 調(diào)換位置猎提。
    推薦按照下面的格式來定義屬性
@property (nonatomic, readwrite, copy) NSString *name;

屬性的參數(shù)應(yīng)該按照下面的順序排列: 原子性,讀寫 和 內(nèi)存管理旁蔼。 這樣做你的屬性更容易修改正確锨苏,并且更好閱讀。這在《禪與Objective-C編程藝術(shù) >》里有介紹棺聊。而且習(xí)慣上修改某個屬性的修飾符時蚓炬,一般從屬性名從右向左搜索需要修動的修飾符。最可能從最右邊開始修改這些屬性的修飾符躺屁,根據(jù)經(jīng)驗(yàn)這些修飾符被修改的可能性從高到底應(yīng)為:內(nèi)存管理 > 讀寫權(quán)限 >原子操作。

硬傷部分

  1. 在-和(void)之間應(yīng)該有一個空格
  2. enum 中駝峰命名法和下劃線命名法混用錯誤:枚舉類型的命名規(guī)則和函數(shù)的命名規(guī)則相同:命名時使用駝峰命名法经宏,勿使用下劃線命名法犀暑。
  3. enum 左括號前加一個空格,或者將左括號換到下一行
  4. enum 右括號后加一個空格
  5. UserModel :NSObject 應(yīng)為UserModel : NSObject烁兰,也就是:右側(cè)少了一個空格耐亏。
  6. @interface@property 屬性聲明中間應(yīng)當(dāng)間隔一行。
  7. 兩個方法定義之間不需要換行沪斟,有時為了區(qū)分方法的功能也可間隔一行广辰,但示例代碼中間隔了兩行。
  8. -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名與參數(shù)之間多了空格主之。而且 -(id) 之間少了空格择吊。
`-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;`方法中方法名與參數(shù)之間多了空格:`(NSString*)name` 前多了空格。
`-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;` 方法中 `(NSString*)name`,應(yīng)為 `(NSString *)name`槽奕,少了空格几睛。 
  1. <p><del>doLogIn方法中的 LogIn 命名不清晰:筆者猜測是login的意思,應(yīng)該是粗心手誤造成的粤攒。
    (勘誤: Login 是名詞所森, LogIn 是動詞,都表示登陸的意思夯接。見: Log in vs. login )</del></p>

2. 什么情況使用 weak 關(guān)鍵字焕济,相比 assign 有什么不同?

什么情況使用 weak 關(guān)鍵字盔几?

  1. 在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性

  2. 自身已經(jīng)對它進(jìn)行一次強(qiáng)引用,沒有必要再強(qiáng)引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak晴弃;當(dāng)然,也可以使用strong。在下文也有論述:《IBOutlet連出來的視圖屬性為什么可以被設(shè)置成weak?》

不同點(diǎn):

  1. weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)肝匆。為這種屬性設(shè)置新值時粒蜈,設(shè)置方法既不保留新值,也不釋放舊值旗国。此特質(zhì)同assign類似枯怖,
    然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)能曾。
    assign 的“設(shè)置方法”只會執(zhí)行針對“純量類型” (scalar type度硝,例如 CGFloat 或
    NSlnteger 等)的簡單賦值操作。

  2. assigin 可以用非 OC 對象,而 weak 必須用于 OC 對象

3. 怎么用 copy 關(guān)鍵字寿冕?

用途:

  1. NSString蕊程、NSArray、NSDictionary 等等經(jīng)常使用copy關(guān)鍵字驼唱,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString藻茂、NSMutableArray、NSMutableDictionary玫恳;
  2. block 也經(jīng)常使用 copy 關(guān)鍵字辨赐,具體原因見官方文檔:Objects Use Properties to Keep Track of Blocks

block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的,但寫上 copy 也無傷大雅京办,還能時刻提醒我們:編譯器自動對 block 進(jìn)行了 copy 操作掀序。如果不寫 copy ,該類的調(diào)用者有可能會忘記或者根本不知道“編譯器會自動對 block 進(jìn)行了 copy 操作”惭婿,他們有可能會在調(diào)用之前自行拷貝屬性值不恭。這種操作多余而低效。

enter image description here
enter image description here

下面做下解釋:
copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似财饥。然而設(shè)置方法并不保留新值换吧,而是將其“拷貝” (copy)。
當(dāng)屬性類型為 NSString 時佑力,經(jīng)常用此特質(zhì)來保護(hù)其封裝性式散,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實(shí)例。這個類是 NSString 的子類打颤,表示一種可修改其值的字符串暴拄,此時若是不拷貝字符串,那么設(shè)置完屬性之后编饺,字符串的值就可能會在對象不知情的情況下遭人更改乖篷。所以,這時就要拷貝一份“不可變” (immutable)的字符串透且,確保對象中的字符串值不會無意間變動撕蔼。只要實(shí)現(xiàn)屬性所用的對象是“可變的” (mutable)豁鲤,就應(yīng)該在設(shè)置新屬性值時拷貝一份。

@property 聲明 NSString鲸沮、NSArray琳骡、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString讼溺、NSMutableArray楣号、NSMutableDictionary,他們之間可能進(jìn)行賦值操作怒坯,為確保對象中的字符串值不會無意間變動炫狱,應(yīng)該在設(shè)置新屬性值時拷貝一份。

該問題在下文中也有論述:用@property聲明的NSString(或NSArray剔猿,NSDictionary)經(jīng)常使用copy關(guān)鍵字视译,為什么?如果改用strong關(guān)鍵字归敬,可能造成什么問題酷含?

4. 這個寫法會出什么問題: @property (copy) NSMutableArray *array;

兩個問題:1、添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因?yàn)檎也坏綄?yīng)的方法而崩潰.因?yàn)?copy 就是復(fù)制一個不可變 NSArray 的對象汪茧;2第美、使用了 atomic 屬性會嚴(yán)重影響性能 ;

第1條的相關(guān)原因在下文中有論述《用@property聲明的NSString(或NSArray陆爽,NSDictionary)經(jīng)常使用 copy 關(guān)鍵字,為什么扳缕?如果改用strong關(guān)鍵字慌闭,可能造成什么問題?》 以及上文《怎么用 copy 關(guān)鍵字躯舔?》也有論述驴剔。

比如下面的代碼就會發(fā)生崩潰

// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 下面的代碼就會發(fā)生崩潰

@property (nonatomic, copy) NSMutableArray *mutableArray;
// .m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 下面的代碼就會發(fā)生崩潰

NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];

接下來就會奔潰:

 -[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460

第2條原因,如下:

該屬性使用了同步鎖粥庄,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序丧失,這會帶來性能問題,通過聲明 nonatomic 可以節(jié)省這些雖然很小但是不必要額外開銷惜互。

在默認(rèn)情況下布讹,由編譯器所合成的方法會通過鎖定機(jī)制確保其原子性(atomicity)。如果屬性具備 nonatomic 特質(zhì)训堆,則不使用同步鎖描验。請注意,盡管沒有名為“atomic”的特質(zhì)(如果某屬性不具備 nonatomic 特質(zhì)坑鱼,那它就是“原子的”(atomic))膘流。

在iOS開發(fā)中,你會發(fā)現(xiàn),幾乎所有屬性都聲明為 nonatomic呼股。

一般情況下并不要求屬性必須是“原子的”耕魄,因?yàn)檫@并不能保證“線程安全” ( thread safety),若要實(shí)現(xiàn)“線程安全”的操作彭谁,還需采用更為深層的鎖定機(jī)制才行吸奴。例如,一個線程在連續(xù)多次讀取某屬性值的過程中有別的線程在同時改寫該值马靠,那么即便將屬性聲明為 atomic奄抽,也還是會讀到不同的屬性值。

因此甩鳄,開發(fā)iOS程序時一般都會使用 nonatomic 屬性逞度。但是在開發(fā) Mac OS X 程序時,使用
atomic 屬性通常都不會有性能瓶頸妙啃。

5. 如何讓自己的類用 copy 修飾符档泽?如何重寫帶 copy 關(guān)鍵字的 setter?

若想令自己所寫的對象具有拷貝功能揖赴,則需實(shí)現(xiàn) NSCopying 協(xié)議馆匿。如果自定義的對象分為可變版本與不可變版本,那么就要同時實(shí)現(xiàn) NSCopyingNSMutableCopying 協(xié)議燥滑。

具體步驟:

  1. 需聲明該類遵從 NSCopying 協(xié)議
  2. 實(shí)現(xiàn) NSCopying 協(xié)議渐北。該協(xié)議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;

注意:一提到讓自己的類用 copy 修飾符,我們總是想覆寫copy方法铭拧,其實(shí)真正需要實(shí)現(xiàn)的卻是 “copyWithZone” 方法赃蛛。

以第一題的代碼為例:

   // .h文件
   // http://weibo.com/luohanchenyilong/
   // https://github.com/ChenYilong
   // 修改完的代碼

   typedef NS_ENUM(NSInteger, CYLSex) {
       CYLSexMan,
       CYLSexWoman
   };

   @interface CYLUser : NSObject<NSCopying>

   @property (nonatomic, readonly, copy) NSString *name;
   @property (nonatomic, readonly, assign) NSUInteger age;
   @property (nonatomic, readonly, assign) CYLSex sex;

   - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
   + (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;

   @end

然后實(shí)現(xiàn)協(xié)議中規(guī)定的方法:

- (id)copyWithZone:(NSZone *)zone {
    CYLUser *copy = [[[self class] allocWithZone:zone] 
                     initWithName:_name
                                  age:_age
                                  sex:_sex];
    return copy;
}

但在實(shí)際的項(xiàng)目中,不可能這么簡單搀菩,遇到更復(fù)雜一點(diǎn)呕臂,比如類對象中的數(shù)據(jù)結(jié)構(gòu)可能并未在初始化方法中設(shè)置好,需要另行設(shè)置肪跋。舉個例子歧蒋,假如 CYLUser 中含有一個數(shù)組,與其他 CYLUser 對象建立或解除朋友關(guān)系的那些方法都需要操作這個數(shù)組州既。那么在這種情況下谜洽,你得把這個包含朋友對象的數(shù)組也一并拷貝過來。下面列出了實(shí)現(xiàn)此功能所需的全部代碼:

// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 以第一題《風(fēng)格糾錯題》里的代碼為例

typedef NS_ENUM(NSInteger, CYLSex) {
    CYLSexMan,
    CYLSexWoman
};

@interface CYLUser : NSObject<NSCopying>

@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) CYLSex sex;

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
- (void)addFriend:(CYLUser *)user;
- (void)removeFriend:(CYLUser *)user;

@end

// .m文件

// .m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
//

@implementation CYLUser {
   NSMutableSet *_friends;
}

- (void)setName:(NSString *)name {
   _name = [name copy];
}

- (instancetype)initWithName:(NSString *)name
                        age:(NSUInteger)age
                        sex:(CYLSex)sex {
   if(self = [super init]) {
       _name = [name copy];
       _age = age;
       _sex = sex;
       _friends = [[NSMutableSet alloc] init];
   }
   return self;
}

- (void)addFriend:(CYLUser *)user {
   [_friends addObject:user];
}

- (void)removeFriend:(CYLUser *)user {
   [_friends removeObject:person];
}

- (id)copyWithZone:(NSZone *)zone {
   CYLUser *copy = [[[self class] allocWithZone:zone]
                    initWithName:_name
                    age:_age
                    sex:_sex];
   copy->_friends = [_friends mutableCopy];
   return copy;
}

- (id)deepCopy {
   CYLUser *copy = [[[self class] allocWithZone:zone]
                    initWithName:_name
                    age:_age
                    sex:_sex];
   copy->_friends = [[NSMutableSet alloc] initWithSet:_friends
                                            copyItems:YES];
   return copy;
}

@end

以上做法能滿足基本的需求吴叶,但是也有缺陷:

如果你所寫的對象需要深拷貝褥琐,那么可考慮新增一個專門執(zhí)行深拷貝的方法称龙。

【注:深淺拷貝的概念疹娶,在下文中有介紹,詳見下文的:用@property聲明的 NSString(或NSArray鞋仍,NSDictionary)經(jīng)常使用 copy 關(guān)鍵字,為什么磕洪?如果改用 strong 關(guān)鍵字吭练,可能造成什么問題?

在例子中析显,存放朋友對象的 set 是用 “copyWithZone:” 方法來拷貝的鲫咽,這種淺拷貝方式不會逐個復(fù)制 set 中的元素。若需要深拷貝的話谷异,則可像下面這樣分尸,編寫一個專供深拷貝所用的方法:

- (id)deepCopy {
   CYLUser *copy = [[[self class] allocWithZone:zone]
                    initWithName:_name
                    age:_age
                    sex:_sex];
   copy->_friends = [[NSMutableSet alloc] initWithSet:_friends
                                            copyItems:YES];
   return copy;
}

至于如何重寫帶 copy 關(guān)鍵字的 setter這個問題,

如果拋開本例來回答的話歹嘹,如下:

- (void)setName:(NSString *)name {
    //[_name release];
    _name = [name copy];
}

不過也有爭議箩绍,有人說“蘋果如果像下面這樣干,是不是效率會高一些尺上?”

- (void)setName:(NSString *)name {
   if (_name != name) {
       //[_name release];//MRC
       _name = [name copy];
   }
}

這樣真得高效嗎材蛛?不見得!這種寫法“看上去很美怎抛、很合理”卑吭,但在實(shí)際開發(fā)中,它更像下圖里的做法:

enter image description here
enter image description here

克強(qiáng)總理這樣評價(jià)你的代碼風(fēng)格:

enter image description here
enter image description here

我和總理的意見基本一致:

老百姓 copy 一下马绝,咋就這么難豆赏?

你可能會說:

之所以在這里做if判斷 這個操作:是因?yàn)橐粋€ if 可能避免一個耗時的copy,還是很劃算的富稻。
(在剛剛講的:《如何讓自己的類用 copy 修飾符河绽?》里的那種復(fù)雜的copy,我們可以稱之為 “耗時的copy”唉窃,但是對 NSString 的 copy 還稱不上。)

但是你有沒有考慮過代價(jià):

你每次調(diào)用 setX: 都會做 if 判斷纹笼,這會讓 setX: 變慢纹份,如果你在 setX:寫了一串復(fù)雜的 if+elseif+elseif+... 判斷,將會更慢廷痘。

要回答“哪個效率會高一些蔓涧?”這個問題,不能脫離實(shí)際開發(fā)笋额,就算 copy 操作十分耗時元暴,if 判斷也不見得一定會更快,除非你把一個“ @property他當(dāng)前的值 ”賦給了他自己兄猩,代碼看起來就像:

[a setX:x1];
[a setX:x1];    //你確定你要這么干茉盏?與其在setter中判斷鉴未,為什么不把代碼寫好?

或者

[a setX:[a x]];   //隊(duì)友咆哮道:你在干嘛鸠姨?M选!

不要在 setter 里進(jìn)行像 if(_obj != newObj) 這樣的判斷讶迁。(該觀點(diǎn)參考鏈接: How To Write Cocoa Object Setters: Principle 3: Only Optimize After You Measure

什么情況會在 copy setter 里做 if 判斷连茧?
例如,車速可能就有最高速的限制巍糯,車速也不可能出現(xiàn)負(fù)值啸驯,如果車子的最高速為300,則 setter 的方法就要改寫成這樣:

-(void)setSpeed:(int)_speed{
    if(_speed < 0) speed = 0;
    if(_speed > 300) speed = 300;
    _speed = speed;
}

回到這個題目祟峦,如果單單就上文的代碼而言罚斗,我們不需要也不能重寫 name 的 setter :由于是 name 是只讀屬性,所以編譯器不會為其創(chuàng)建對應(yīng)的“設(shè)置方法”搀愧,用初始化方法設(shè)置好屬性值之后惰聂,就不能再改變了。( 在本例中咱筛,之所以還要聲明屬性的“內(nèi)存管理語義”--copy搓幌,是因?yàn)椋喝绻粚?copy,該類的調(diào)用者就不知道初始化方法里會拷貝這些屬性迅箩,他們有可能會在調(diào)用初始化方法之前自行拷貝屬性值溉愁。這種操作多余而低效)。

那如何確保 name 被 copy饲趋?在初始化方法(initializer)中做:

   - (instancetype)initWithName:(NSString *)name 
                                age:(NSUInteger)age 
                                sex:(CYLSex)sex {
        if(self = [super init]) {
           _name = [name copy];
           _age = age;
           _sex = sex;
           _friends = [[NSMutableSet alloc] init];
        }
        return self;
   }

6. @property 的本質(zhì)是什么拐揭?ivar、getter奕塑、setter 是如何生成并添加到這個類中的

@property 的本質(zhì)是什么堂污?

@property = ivar + getter + setter;

下面解釋下:

“屬性” (property)有兩大概念:ivar(實(shí)例變量)、存取方法(access method = getter + setter)龄砰。

“屬性” (property)作為 Objective-C 的一項(xiàng)特性盟猖,主要的作用就在于封裝對象中的數(shù)據(jù)。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實(shí)例變量换棚。實(shí)例變量一般通過“存取方法”(access method)來訪問式镐。其中,“獲取方法” (getter)用于讀取變量值固蚤,而“設(shè)置方法” (setter)用于寫入變量值娘汞。這個概念已經(jīng)定型,并且經(jīng)由“屬性”這一特性而成為 Objective-C 2.0 的一部分夕玩。
而在正規(guī)的 Objective-C 編碼風(fēng)格中你弦,存取方法有著嚴(yán)格的命名規(guī)范惊豺。
正因?yàn)橛辛诉@種嚴(yán)格的命名規(guī)范,所以 Objective-C 這門語言才能根據(jù)名稱自動創(chuàng)建出存取方法鳖目。其實(shí)也可以把屬性當(dāng)做一種關(guān)鍵字扮叨,其表示:

編譯器會自動寫出一套存取方法,用以訪問給定類型中具有給定名稱的變量领迈。
所以你也可以這么說:

@property = getter + setter;

例如下面這個類:

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

上述代碼寫出來的類與下面這種寫法等效:

@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end

ivar彻磁、getter、setter 是如何生成并添加到這個類中的?

“自動合成”( autosynthesis)

完成屬性定義后狸捅,編譯器會自動編寫訪問這些屬性所需的方法衷蜓,此過程叫做“自動合成”(autosynthesis)。需要強(qiáng)調(diào)的是尘喝,這個過程由編譯
器在編譯期執(zhí)行磁浇,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter朽褪、setter 之外置吓,編譯器還要自動向類中添加適當(dāng)類型的實(shí)例變量,并且在屬性名前面加下劃線缔赠,以此作為實(shí)例變量的名字衍锚。在前例中,會生成兩個實(shí)例變量嗤堰,其名稱分別為
_firstName_lastName戴质。也可以在類的實(shí)現(xiàn)代碼里通過
@synthesize 語法來指定實(shí)例變量的名字.

@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end

我為了搞清屬性是怎么實(shí)現(xiàn)的,曾經(jīng)反編譯過相關(guān)的代碼,他大致生成了五個東西

  1. OBJC_IVAR_$類名$屬性名稱 :該屬性的“偏移量” (offset),這個偏移量是“硬編碼” (hardcode)踢匣,表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠(yuǎn)告匠。
  2. setter 與 getter 方法對應(yīng)的實(shí)現(xiàn)函數(shù)
  3. ivar_list :成員變量列表
  4. method_list :方法列表
  5. prop_list :屬性列表

也就是說我們每次在增加一個屬性,系統(tǒng)都會在 ivar_list 中添加一個成員變量的描述,在 method_list 中增加 setter 與 getter 方法的描述,在屬性列表中增加一個屬性的描述,然后計(jì)算該屬性在對象中的偏移量,然后給出 setter 與 getter 方法對應(yīng)的實(shí)現(xiàn),在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值,為了能夠讀取正確字節(jié)數(shù),系統(tǒng)對象偏移量的指針類型進(jìn)行了類型強(qiáng)轉(zhuǎn).

7. @protocol 和 category 中如何使用 @property

  1. 在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實(shí)現(xiàn)該屬性

  2. category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時的兩個函數(shù):

  3. objc_setAssociatedObject

  4. objc_getAssociatedObject

8. runtime 如何實(shí)現(xiàn) weak 屬性

要實(shí)現(xiàn) weak 屬性,首先要搞清楚 weak 屬性的特點(diǎn):

weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)离唬。為這種屬性設(shè)置新值時后专,設(shè)置方法既不保留新值,也不釋放舊值输莺。此特質(zhì)同 assign 類似戚哎, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)模闲。

那么 runtime 如何實(shí)現(xiàn) weak 變量的自動置nil?

runtime 對注冊的類崭捍, 會進(jìn)行布局尸折,對于 weak 對象會放入一個 hash 表中。 用 weak 指向的對象內(nèi)存地址作為 key殷蛇,當(dāng)此對象的引用計(jì)數(shù)為0的時候會 dealloc实夹,假如 weak 指向的對象內(nèi)存地址是a橄浓,那么就會以a為鍵, 在這個 weak 表中搜索亮航,找到所有以a為鍵的 weak 對象荸实,從而設(shè)置為 nil。

(注:在下文的《使用runtime Associate方法關(guān)聯(lián)的對象缴淋,需要在主對象dealloc的時候釋放么准给?》里給出的“對象的內(nèi)存銷毀時間表”也提到__weak引用的解除時間。)

我們可以設(shè)計(jì)一個函數(shù)(偽代碼)來表示上述機(jī)制:

objc_storeWeak(&a, b)函數(shù):

objc_storeWeak函數(shù)把第二個參數(shù)--賦值對象(b)的內(nèi)存地址作為鍵值key重抖,將第一個參數(shù)--weak修飾的屬性變量(a)的內(nèi)存地址(&a)作為value露氮,注冊到 weak 表中。如果第二個參數(shù)(b)為0(nil)钟沛,那么把變量(a)的內(nèi)存地址(&a)從weak表中刪除畔规,

你可以把objc_storeWeak(&a, b)理解為:objc_storeWeak(value, key),并且當(dāng)key變nil恨统,將value置nil叁扫。

在b非nil時,a和b指向同一個內(nèi)存地址畜埋,在b變nil時莫绣,a變nil。此時向a發(fā)送消息不會崩潰:在Objective-C中向nil發(fā)送消息是安全的由捎。

而如果a是由 assign 修飾的兔综,則:
在 b 非 nil 時,a 和 b 指向同一個內(nèi)存地址狞玛,在 b 變 nil 時软驰,a 還是指向該內(nèi)存地址,變野指針心肪。此時向 a 發(fā)送消息極易崩潰锭亏。

下面我們將基于objc_storeWeak(&a, b)函數(shù),使用偽代碼模擬“runtime如何實(shí)現(xiàn)weak屬性”:

// 使用偽代碼模擬:runtime如何實(shí)現(xiàn)weak屬性
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong

 id obj1;
 objc_initWeak(&obj1, obj);
/*obj引用計(jì)數(shù)變?yōu)?硬鞍,變量作用域結(jié)束*/
 objc_destroyWeak(&obj1);

下面對用到的兩個方法objc_initWeakobjc_destroyWeak做下解釋:

總體說來慧瘤,作用是:
通過objc_initWeak函數(shù)初始化“附有weak修飾符的變量(obj1)”,在變量作用域結(jié)束時通過objc_destoryWeak函數(shù)釋放該變量(obj1)固该。

下面分別介紹下方法的內(nèi)部實(shí)現(xiàn):

objc_initWeak函數(shù)的實(shí)現(xiàn)是這樣的:在將“附有weak修飾符的變量(obj1)”初始化為0(nil)后锅减,會將“賦值對象”(obj)作為參數(shù),調(diào)用objc_storeWeak函數(shù)伐坏。

obj1 = 0怔匣;
obj_storeWeak(&obj1, obj);

也就是說:

weak 修飾的指針默認(rèn)值是 nil (在Objective-C中向nil發(fā)送消息是安全的)

然后obj_destroyWeak函數(shù)將0(nil)作為參數(shù),調(diào)用objc_storeWeak函數(shù)桦沉。

objc_storeWeak(&obj1, 0);

前面的源代碼與下列源代碼相同每瞒。

// 使用偽代碼模擬:runtime如何實(shí)現(xiàn)weak屬性
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong

id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
/* ... obj的引用計(jì)數(shù)變?yōu)?金闽,被置nil ... */
objc_storeWeak(&obj1, 0);

objc_storeWeak 函數(shù)把第二個參數(shù)--賦值對象(obj)的內(nèi)存地址作為鍵值,將第一個參數(shù)--weak修飾的屬性變量(obj1)的內(nèi)存地址注冊到 weak 表中剿骨。如果第二個參數(shù)(obj)為0(nil)代芜,那么把變量(obj1)的地址從 weak 表中刪除,在后面的相關(guān)一題會詳解浓利。

使用偽代碼是為了方便理解挤庇,下面我們“真槍實(shí)彈”地實(shí)現(xiàn)下:

如何讓不使用weak修飾的@property,擁有weak的效果荞膘。

我們從setter方法入手:

- (void)setObject:(NSObject *)object
{
   objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
   [object cyl_runAtDealloc:^{
       _object = nil;
   }];
}

也就是有兩個步驟:

  1. 在setter方法中做如下設(shè)置:
       objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
  1. 在屬性所指的對象遭到摧毀時罚随,屬性值也會清空(nil out)。做到這點(diǎn)羽资,同樣要借助 runtime:
//要銷毀的目標(biāo)對象
id objectToBeDeallocated;
//可以理解為一個“事件”:當(dāng)上面的目標(biāo)對象銷毀時淘菩,同時要發(fā)生的“事件”。
id objectWeWantToBeReleasedWhenThatHappens;
objc_setAssociatedObject(objectToBeDeallocted,
                        someUniqueKey,
                        objectWeWantToBeReleasedWhenThatHappens,
                        OBJC_ASSOCIATION_RETAIN);

知道了思路屠升,我們就開始實(shí)現(xiàn) cyl_runAtDealloc 方法潮改,實(shí)現(xiàn)過程分兩部分:

第一部分:創(chuàng)建一個類,可以理解為一個“事件”:當(dāng)目標(biāo)對象銷毀時腹暖,同時要發(fā)生的“事件”汇在。借助 block 執(zhí)行“事件”。

// .h文件

// .h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 這個類脏答,可以理解為一個“事件”:當(dāng)目標(biāo)對象銷毀時糕殉,同時要發(fā)生的“事件”。借助block執(zhí)行“事件”殖告。

typedef void (^voidBlock)(void);

@interface CYLBlockExecutor : NSObject

- (id)initWithBlock:(voidBlock)block;

@end

// .m文件

// .m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 這個類阿蝶,可以理解為一個“事件”:當(dāng)目標(biāo)對象銷毀時,同時要發(fā)生的“事件”黄绩。借助block執(zhí)行“事件”羡洁。

#import "CYLBlockExecutor.h"

@interface CYLBlockExecutor() {
   voidBlock _block;
}
@implementation CYLBlockExecutor

- (id)initWithBlock:(voidBlock)aBlock
{
   self = [super init];
   
   if (self) {
       _block = [aBlock copy];
   }
   
   return self;
}

- (void)dealloc
{
   _block ? _block() : nil;
}

@end

第二部分:核心代碼:利用runtime實(shí)現(xiàn)cyl_runAtDealloc方法

// CYLNSObject+RunAtDealloc.h文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 利用runtime實(shí)現(xiàn)cyl_runAtDealloc方法

#import "CYLBlockExecutor.h"

const void *runAtDeallocBlockKey = &runAtDeallocBlockKey;

@interface NSObject (CYLRunAtDealloc)

- (void)cyl_runAtDealloc:(voidBlock)block;

@end


// CYLNSObject+RunAtDealloc.m文件
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// 利用runtime實(shí)現(xiàn)cyl_runAtDealloc方法

#import "CYLNSObject+RunAtDealloc.h"
#import "CYLBlockExecutor.h"

@implementation NSObject (CYLRunAtDealloc)

- (void)cyl_runAtDealloc:(voidBlock)block
{
   if (block) {
       CYLBlockExecutor *executor = [[CYLBlockExecutor alloc] initWithBlock:block];
       
       objc_setAssociatedObject(self,
                                runAtDeallocBlockKey,
                                executor,
                                OBJC_ASSOCIATION_RETAIN);
   }
}

@end

使用方法:
導(dǎo)入

   #import "CYLNSObject+RunAtDealloc.h"

然后就可以使用了:

NSObject *foo = [[NSObject alloc] init];

[foo cyl_runAtDealloc:^{
   NSLog(@"正在釋放foo!");
}];

如果對 cyl_runAtDealloc 的實(shí)現(xiàn)原理有興趣,可以看下這篇博文 Fun With the Objective-C Runtime: Run Code at Deallocation of Any Object

9. @property中有哪些屬性關(guān)鍵字爽丹?/ @property 后面可以有哪些修飾符筑煮?

屬性可以擁有的特質(zhì)分為四類:

  1. 原子性--- nonatomic 特質(zhì)

    在默認(rèn)情況下,由編譯器合成的方法會通過鎖定機(jī)制確保其原子性(atomicity)粤蝎。如果屬性具備 nonatomic 特質(zhì)真仲,則不使用同步鎖。請注意初澎,盡管沒有名為“atomic”的特質(zhì)(如果某屬性不具備 nonatomic 特質(zhì)秸应,那它就是“原子的” ( atomic) ),但是仍然可以在屬性特質(zhì)中寫明這一點(diǎn),編譯器不會報(bào)錯灸眼。若是自己定義存取方法,那么就應(yīng)該遵從與屬性特質(zhì)相符的原子性墓懂。

  2. 讀/寫權(quán)限---readwrite(讀寫)焰宣、readonly (只讀)

  3. 內(nèi)存管理語義---assignstrong捕仔、 weak匕积、unsafe_unretainedcopy

  4. 方法名---getter=<name> 榜跌、setter=<name>

getter=<name>的樣式:

    @property (nonatomic, getter=isOn) BOOL on;

setter=<name>這種不常用闪唆,也不推薦使用。故不在這里給出寫法钓葫。)

  1. 不常用的:nonnull,null_resettable,nullable

10. weak屬性需要在dealloc中置nil么悄蕾?

不需要。

在ARC環(huán)境無論是強(qiáng)指針還是弱指針都無需在 dealloc 設(shè)置為 nil 础浮, ARC 會自動幫我們處理

即便是編譯器不幫我們做這些帆调,weak也不需要在 dealloc 中置nil:

正如上文的:runtime 如何實(shí)現(xiàn) weak 屬性 中提到的:

我們模擬下 weak 的 setter 方法,應(yīng)該如下:

- (void)setObject:(NSObject *)object
{
   objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
   [object cyl_runAtDealloc:^{
       _object = nil;
   }];
}

也即:

在屬性所指的對象遭到摧毀時豆同,屬性值也會清空(nil out)番刊。

11. @synthesize和@dynamic分別有什么作用?

  1. @property有兩個對應(yīng)的詞影锈,一個是 @synthesize芹务,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫鸭廷,那么默認(rèn)的就是@syntheszie var = _var;
  2. @synthesize 的語義是如果你沒有手動實(shí)現(xiàn) setter 方法和 getter 方法枣抱,那么編譯器會自動為你加上這兩個方法。
  3. @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn)靴姿,不自動生成沃但。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)。假如一個屬性被聲明為 @dynamic var佛吓,然后你沒有提供 @setter方法和 @getter 方法宵晚,編譯的時候沒問題,但是當(dāng)程序運(yùn)行到 instance.var = someVar维雇,由于缺 setter 方法會導(dǎo)致程序崩潰淤刃;或者當(dāng)運(yùn)行到 someVar = var 時,由于缺 getter 方法同樣會導(dǎo)致崩潰吱型。編譯時沒問題逸贾,運(yùn)行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定。

12. ARC下铝侵,不顯式指定任何屬性關(guān)鍵字時灼伤,默認(rèn)的關(guān)鍵字都有哪些?

  1. 對應(yīng)基本數(shù)據(jù)類型默認(rèn)關(guān)鍵字是

atomic,readwrite,assign

  1. 對于普通的 Objective-C 對象

atomic,readwrite,strong

參考鏈接:

  1. Objective-C ARC: strong vs retain and weak vs assign

  2. Variable property attributes or Modifiers in iOS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咪鲜,一起剝皮案震驚了整個濱河市狐赡,隨后出現(xiàn)的幾起案子胜卤,更是在濱河造成了極大的恐慌尚揣,老刑警劉巖屑墨,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煮寡,死亡現(xiàn)場離奇詭異兽叮,居然都是意外死亡骄蝇,警方通過查閱死者的電腦和手機(jī)靡努,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門扭仁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炊琉,“玉大人展蒂,你說我怎么就攤上這事√洌” “怎么了玄货?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悼泌。 經(jīng)常有香客問我松捉,道長,這世上最難降的妖魔是什么馆里? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任隘世,我火速辦了婚禮,結(jié)果婚禮上鸠踪,老公的妹妹穿的比我還像新娘丙者。我一直安慰自己,他們只是感情好营密,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布械媒。 她就那樣靜靜地躺著,像睡著了一般评汰。 火紅的嫁衣襯著肌膚如雪纷捞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天被去,我揣著相機(jī)與錄音主儡,去河邊找鬼。 笑死惨缆,一個胖子當(dāng)著我的面吹牛糜值,可吹牛的內(nèi)容都是我干的丰捷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼寂汇,長吁一口氣:“原來是場噩夢啊……” “哼病往!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骄瓣,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤荣恐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后累贤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡少漆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年臼膏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片示损。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡渗磅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出检访,到底是詐尸還是另有隱情始鱼,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布脆贵,位于F島的核電站医清,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏卖氨。R本人自食惡果不足惜会烙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望筒捺。 院中可真熱鬧柏腻,春花似錦、人聲如沸系吭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肯尺。三九已至沃缘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間则吟,已是汗流浹背孩灯。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逾滥,地道東北人峰档。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓败匹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親讥巡。 傳聞我的和親對象是個殘疾皇子掀亩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 出題者簡介: 孫源(sunnyxx),目前就職于百度欢顷,負(fù)責(zé)百度知道 iOS 客戶端的開發(fā)工作槽棍,對技術(shù)喜歡刨根問底和...
    戈多_于勒閱讀 1,790評論 0 5
  • “三世諸佛,色不異空,空不異色,乃至無意識界,無無明,亦無無明盡,乃至無老死,亦無老死盡,無苦集滅道, 無智亦無得...
    蕎二熊閱讀 394評論 2 3
  • 年少時炼七,總以為世界是我一個人的, 年紀(jì)大了布持,才發(fā)現(xiàn)我只是世界上的一個人
    丹彤木閱讀 205評論 0 1
  • 1今天家里有小寶貝的豌拙,都感嘆,什么時候才能熬到可以每天都有時間睡到7點(diǎn)题暖,我說熬到睡不著天一亮就醒的時候按傅,你們就有時...
    風(fēng)箏2017閱讀 181評論 0 0
  • 昨天給寶寶洗頭洗澡,居然沒有哭鬧胧卤,安安靜靜地唯绍,感覺很享受這個過程。之前月嫂阿姨給小寶洗頭的時候枝誊,小寶一直哇哇大哭况芒,...
    王小蝸閱讀 140評論 0 0