OC屬性的修飾符

屬性修飾符是什么辆憔?有什么作用?

屬性修飾符涤躲,顧名思義就是對(duì)屬性進(jìn)行修飾的符號(hào)棺耍。不同修飾符修飾的屬性會(huì)表現(xiàn)出不一樣的屬性特性,其表現(xiàn)形式并不是那么顯而易見种樱。
那它的作用是什么呢蒙袍?從上面的定義上面我們就能發(fā)現(xiàn),被不同修飾符修飾的屬性會(huì)有不同的特性嫩挤,根據(jù)這些特性害幅,我們能更好的優(yōu)化自己的代碼邏輯,更簡(jiǎn)單的實(shí)現(xiàn)效果俐镐。
接下來(lái)我們就逐個(gè)詳細(xì)分析一下。

屬性修飾符有哪些哺哼?

這里我把常用的屬性修飾符分為三大類

  • 線程安全類 nonatomic/atomic
  • 讀寫權(quán)限類 readwrite/readonly
  • 內(nèi)存管理類 assign/copy/strong/weak

還有其他的例如

  • class 類屬性
  • setter=/getter= 自定義setter/getter方法名
  • retain MRC下內(nèi)存管理

現(xiàn)在我們來(lái)詳細(xì)說(shuō)一下常用的三大類屬性修飾符佩抹。

1、線程安全類 nonatomic/atomic

  1. nonatomic 非原子屬性取董。它的特點(diǎn)是多線程并發(fā)訪問性能高棍苹,但是訪問不安全;與之相對(duì)的就是atomic茵汰,特點(diǎn)就是安全但是是以耗費(fèi)系統(tǒng)資源為代價(jià)枢里,所以一般在工程開發(fā)中用nonatomic的時(shí)候比較多。
  2. 系統(tǒng)默認(rèn)的是atomic蹂午,為setter方法加鎖栏豺,而nonatomic 不為setter方法加鎖。
  3. 如1所述豆胸,使用nonatomic要注意多線程間通信的線程安全奥洼。

根據(jù)上述描述,項(xiàng)目中我們基本上只使用nonatomic晚胡,因?yàn)樗脑L問性能高灵奖,對(duì)于多線程處理的時(shí)候嚼沿,也建議自己去實(shí)現(xiàn)加鎖的處理。一是因?yàn)?code>atomic的加鎖占用系統(tǒng)資源量大瓷患,二是因?yàn)?code>atomic只是在setter/getter方法中進(jìn)行了加鎖處理骡尽,在其他操作中是沒有的,這里可能會(huì)出現(xiàn)遺漏擅编。

2攀细、讀寫權(quán)限類 readwrite/readonly
這個(gè)就是訪問權(quán)限的控制,決定該屬性是否可讀和可寫沙咏,默認(rèn)是readwrite辨图,所以我們定義屬性的時(shí)候,一般不需要這個(gè)修飾肢藐。只有只讀屬性才需要加上readonly的修飾故河。
readonly來(lái)控制讀寫權(quán)限的方式就是只生成getter方法,不生成setter方法吆豹。

3鱼的、內(nèi)存管理類 assign/copy/strong/weak
這一類的的修飾符是該文檔中最重要的部分了,這也是我們項(xiàng)目編碼中痘煤,經(jīng)常會(huì)混淆凑阶,理解錯(cuò)誤的地方。不知道這些修飾符都要什么時(shí)候時(shí)候使用衷快。

  • assign
    該修飾符是給那些不需要進(jìn)行內(nèi)存管理的變量使用的宙橱,包括所有的基礎(chǔ)類型變量,例如 int float double long BOOL NSInteger 等蘸拔。還有的是由棧區(qū)师郑,全局區(qū),常量區(qū)管理的變量也可以使用assign來(lái)修飾调窍,因?yàn)樗麄兊膬?nèi)存已經(jīng)被系統(tǒng)自動(dòng)管理了宝冕,無(wú)需手動(dòng)額外管理。

  • copy
    該修飾符修飾的屬性在賦值的時(shí)候會(huì)多一個(gè)執(zhí)行copy方法的操作邓萨,對(duì)于copy的方法實(shí)現(xiàn)完全按照對(duì)應(yīng)的對(duì)象的copy實(shí)現(xiàn)地梨,如NSString的copy是不變的。
    對(duì)于那些屬性賦值需要進(jìn)行淺拷貝的缔恳,也就是當(dāng)前類中處理該屬性的值不影響傳入變量的情況下宝剖,可以使用該類。但是要謹(jǐn)慎使用歉甚,很容易會(huì)產(chǎn)生問題诈闺,例如可變類型不能使用copy來(lái)進(jìn)行修飾,否則賦值的變量都會(huì)變成不可變類型的屬性铃芦,與原先定義的屬性類型不一致雅镊。在編寫代碼過程中可能會(huì)調(diào)用不屬與該類的方法襟雷。

  • strong
    該修飾符相當(dāng)于是MRC中的 retain 修飾符。它是一種強(qiáng)引用仁烹,被它修飾的屬性都會(huì)進(jìn)行內(nèi)存管理耸弄,也就是引用計(jì)數(shù)的管理。

  • weak
    弱引用卓缰,和 strong 相對(duì)應(yīng)计呈。常用于解決循環(huán)引用問題。被修飾的屬性在其他持有者都被釋放之后征唬,該屬性會(huì)自動(dòng)指向 nil捌显,也就是說(shuō),作用和assign一樣总寒,但是更加安全扶歪!

總結(jié):可以說(shuō)屬性修飾符就是對(duì)setter/getter方法的一些功能性擴(kuò)展,使其定義簡(jiǎn)單的同時(shí)能夠滿足更多的功能要求摄闸。

棧區(qū)善镰、常量區(qū)、全局區(qū)用 assign
堆區(qū)持有用 strong年枕、copy炫欺,不持有用 weak
什么叫不持有?
就是一個(gè)對(duì)象別其他變量持有了熏兄,那該變量就指向該對(duì)象品洛,如果別的變量都不持有該對(duì)象了,那該變量也不需要指向該對(duì)象了摩桶,這就叫不持有桥状。

注意點(diǎn)

接下來(lái)我們說(shuō)明一下在實(shí)際使用過程中比較常見的一些誤區(qū)和知識(shí)點(diǎn)。

  1. NSString 為什么要用 copy典格?為什么不能用strong岛宦?
    如果是簡(jiǎn)單的就是 NSString 類型的字符串台丛,它們都是存放在常量區(qū)里面 的耍缴,就不需要進(jìn)行內(nèi)存管理,assign,copy,strong 都是可以修飾的挽霉,它們實(shí)際上都不會(huì)去改變?nèi)魏螙|西防嗡。
@property (nonatomic, assign) NSString *str1;
@property (nonatomic, copy) NSString *str2;
@property (nonatomic, strong) NSString *str3;
    self.str1 = @"str1";
    self.str2 = @"str2";
    self.str3 = @"str3";
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"str1:%@ %p", self.str1, self.str1);
        NSLog(@"str2:%@ %p", self.str2, self.str2);
        NSLog(@"str3:%@ %p", self.str3, self.str3);
    });
2020-07-01 14:37:16.465702+0800 Test_UI[76089:3507003] str1:str1 0x10c1d5150
2020-07-01 14:37:16.465822+0800 Test_UI[76089:3507003] str2:str2 0x10c1d5170
2020-07-01 14:37:16.465888+0800 Test_UI[76089:3507003] str3:str3 0x10c1d5190

但是為什么只能用 copy 呢?因?yàn)?NSString 的子類 NSMutableString侠坎,他是存放在堆區(qū)的蚁趁。子類也是可以用父類來(lái)接收的,這中情況下 assign 就不能使用了实胸,它無(wú)法持有 NSMutableString 會(huì)導(dǎo)致被提前釋放他嫡。不使用 strong 而使用 copy 是為了在賦值的時(shí)候去除可變的特性番官,優(yōu)化內(nèi)存管理,讓存放在堆區(qū)的 NSMutableString 對(duì)象及時(shí)得到釋放钢属。

@property (nonatomic, assign) NSString *str1;
@property (nonatomic, copy) NSString *str2;
@property (nonatomic, strong) NSString *str3;
    self.str1 = [@"str1" mutableCopy];
    self.str2 = [@"str2" mutableCopy];
    self.str3 = [@"str3" mutableCopy];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"str1:%@ %p", self.str1, self.str1);
        NSLog(@"str2:%@ %p", self.str2, self.str2);
        NSLog(@"str3:%@ %p", self.str3, self.str3);
        NSLog(@"str class: %@", [@"str123" class]);
        NSLog(@"mutable str copy class : %@", [[[@"str123" mutableCopy] copy] class]);
    });
2020-07-01 14:38:10.159028+0800 Test_UI[76124:3508299] str1:str3 0x6000012d9e00
2020-07-01 14:38:10.159150+0800 Test_UI[76124:3508299] str2:str2 0xaffe863aa45e1a58
2020-07-01 14:38:10.159220+0800 Test_UI[76124:3508299] str3:str3 0x6000012d9e00
2020-07-01 14:38:10.159310+0800 Test_UI[76124:3508299] str class: __NSCFConstantString
2020-07-01 14:38:10.159393+0800 Test_UI[76124:3508299] mutable str copy class : NSTaggedPointerString

我們來(lái)分析一下上面輸出的情況可以看到 str1str3 的地址變成一樣的了徘熔,str1 并沒有變成野指針崩潰,然后我同時(shí)也打印了一下內(nèi)容淆党,發(fā)現(xiàn) str1 的內(nèi)容變成了 str3的內(nèi)容酷师。這是為什么呢?
因?yàn)槲覀兪褂?assign 修飾的 str1 染乌,在賦值之后沒有持有導(dǎo)致馬上就被釋放掉了山孔,而該屬性指向的內(nèi)容也沒有了,而在之后 str3 創(chuàng)建的對(duì)象剛好也是從這個(gè)地址開始創(chuàng)建的荷憋,所以就造成了上面的那種現(xiàn)象台颠。這里我們把后面 str2,str3 的代碼刪除之后運(yùn)行會(huì)發(fā)現(xiàn), str1的內(nèi)容就變成空了台谊。

2020-07-01 15:13:42.852822+0800 Test_UI[92383:3561873] str1:<__NSMallocBlock__: 0x60000089e220> 0x60000089e220

接下來(lái)我們發(fā)現(xiàn) str2 的地址不是在常量區(qū)的蓉媳,這是為什么呢?
最后我們也打印了一下兩種字符串的類型锅铅,發(fā)現(xiàn)它們是不一樣的酪呻,為什么呢?同樣都是不可變字符串盐须,還都是 NSString玩荠。
這是因?yàn)?NSString 是另一個(gè)類蔟,類蔟的定義和表現(xiàn)我們之后再講贼邓。__NSCFConstantString 該類型的會(huì)直接存放在常量區(qū)阶冈,這是代碼編譯時(shí)就能夠決定的,然后直接存放在常量區(qū)塑径。NSTaggedPointerString 這種類型的可以說(shuō)是動(dòng)態(tài)生成的女坑,編譯的時(shí)候無(wú)法判斷,所以在他產(chǎn)生的時(shí)候 動(dòng)態(tài)添加到棧區(qū)里面统舀。

  1. 可變類型不能使用 copy匆骗。
    可變類型的屬性如果使用 copy 來(lái)修飾,在賦值的時(shí)候誉简,該值會(huì)進(jìn)行一次 copy 操作碉就,導(dǎo)致屬性指向的是一個(gè)不可變的對(duì)象,如果用該屬性去調(diào)用可變對(duì)象的方法闷串,會(huì)產(chǎn)生崩潰瓮钥。

  2. Block到底使用什么來(lái)修飾?
    block有一個(gè)特性,當(dāng)它訪問了外部局部變量(注意:這里是外部的局部變量碉熄,全局變量不受影響)桨武,就會(huì)存放在堆區(qū)。

@property (nonatomic, assign) void(^block1)(void);
@property (nonatomic, copy) void(^block2)(void);
@property (nonatomic, strong) void(^block3)(void);
    void(^block1)(void) = ^() {
        NSLog(@"1");
    };
    void(^block2)(void) = ^() {
        NSLog(@"2");
    };
    void(^block3)(void) = ^() {
        NSLog(@"3");
    };
    NSLog(@"block1: %p", block1);
    NSLog(@"block2: %p", block2);
    NSLog(@"block3: %p", block3);
    self.block1 = block1;
    self.block2 = block2;
    self.block3 = block3;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"block1: %p", self.block1);
        NSLog(@"block2: %p", self.block2);
        NSLog(@"block3: %p", self.block3);
    });
2020-07-01 16:39:06.271597+0800 Test_UI[24869:3706043] block1: 0x10a984108
2020-07-01 16:39:06.271695+0800 Test_UI[24869:3706043] block2: 0x10a984128
2020-07-01 16:39:06.271759+0800 Test_UI[24869:3706043] block3: 0x10a984148
2020-07-01 16:39:06.294224+0800 Test_UI[24869:3706043] block1: 0x10a984108
2020-07-01 16:39:06.294314+0800 Test_UI[24869:3706043] block2: 0x10a984128
2020-07-01 16:39:06.294391+0800 Test_UI[24869:3706043] block3: 0x10a984148

所以這樣的block所有修飾符都可以修飾锈津。

@property (nonatomic, assign) void(^block1)(void);
@property (nonatomic, copy) void(^block2)(void);
@property (nonatomic, strong) void(^block3)(void);
    int num = 2;

    void(^block1)(void) = ^() {
        NSLog(@"%d", num);
    };
    void(^block2)(void) = ^() {
        NSLog(@"%d", num);
    };
    void(^block3)(void) = ^() {
        NSLog(@"%d", num);
    };
    NSLog(@"block1: %p", block1);
    NSLog(@"block2: %p", block2);
    NSLog(@"block3: %p", block3);
    self.block1 = block1;
    self.block2 = block2;
    self.block3 = block3;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"block1: %p", self.block1);
        NSLog(@"block2: %p", self.block2);
        NSLog(@"block3: %p", self.block3);
    });
2020-07-01 17:08:27.093104+0800 Test_UI[25619:3736522] block1: 0x600003fb1950
2020-07-01 17:08:27.093214+0800 Test_UI[25619:3736522] block2: 0x600003fb12f0
2020-07-01 17:08:27.093287+0800 Test_UI[25619:3736522] block3: 0x600003fb08d0
2020-07-01 17:08:27.102584+0800 Test_UI[25619:3736522] block1: 0x600003fb1950
2020-07-01 17:08:27.102696+0800 Test_UI[25619:3736522] block2: 0x600003fb12f0
2020-07-01 17:08:27.102785+0800 Test_UI[25619:3736522] block3: 0x600003fb08d0

這種block就不能使用 assign 來(lái)修飾玻募,如果及時(shí)釋放的話,訪問 self.block1 的時(shí)候會(huì)產(chǎn)生崩潰一姿。

報(bào)錯(cuò)

而同樣的我們也可以看到七咧,不管是 copy 還是 strong,block的地址都沒有變叮叹,所以它們是等價(jià)的艾栋,而使用 strong 更加直接,性能會(huì)更好蛉顽,而同樣的蝗砾,對(duì)已經(jīng)自動(dòng)管理的block類型而言,我們所有修飾符都可以使用携冤,所以為了通用悼粮,我們?cè)贏RC下使用 strong 來(lái)修飾所有的block,當(dāng)然也可以用 copy曾棕,可以說(shuō) copy 的修飾是從MRC中繼承過來(lái)的扣猫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市翘地,隨后出現(xiàn)的幾起案子申尤,更是在濱河造成了極大的恐慌,老刑警劉巖衙耕,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昧穿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡橙喘,警方通過查閱死者的電腦和手機(jī)时鸵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)厅瞎,“玉大人饰潜,你說(shuō)我怎么就攤上這事〈沤保” “怎么了囊拜?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵某筐,是天一觀的道長(zhǎng)比搭。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么身诺? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任蜜托,我火速辦了婚禮,結(jié)果婚禮上霉赡,老公的妹妹穿的比我還像新娘橄务。我一直安慰自己,他們只是感情好穴亏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布蜂挪。 她就那樣靜靜地躺著,像睡著了一般嗓化。 火紅的嫁衣襯著肌膚如雪棠涮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天刺覆,我揣著相機(jī)與錄音严肪,去河邊找鬼。 笑死谦屑,一個(gè)胖子當(dāng)著我的面吹牛驳糯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氢橙,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼酝枢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了悍手?” 一聲冷哼從身側(cè)響起隧枫,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谓苟,沒想到半個(gè)月后官脓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涝焙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年卑笨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仑撞。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赤兴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出隧哮,到底是詐尸還是另有隱情桶良,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布沮翔,位于F島的核電站陨帆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疲牵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一承二、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纲爸,春花似錦亥鸠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至颓哮,卻和暖如春盖桥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背题翻。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工揩徊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嵌赠。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓塑荒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親姜挺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子齿税,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348