《iOS Tips 一》

主要內(nèi)容有三:

  1. copy VS strong
  2. newName
  3. __attribute__

Tip1:對(duì)于 NSString 而言,@property 中的 strong 和 copy 有什么區(qū)別 舰攒?

在項(xiàng)目中發(fā)現(xiàn)有人用 strong熬苍,有人用 copy葛假,還有混著用的蝗罗。
問之躁锁,為什么要用strong, 為什么這么寫 ?
答曰:一樣的都是拄显。
so 真的一樣嗎裁蚁?我們細(xì)細(xì)來看矢渊,這里先說有什么區(qū)別:

首先,聲明一個(gè) MLPerson:

//MLPerson.h
#import <Foundation/Foundation.h>
@interface MLPerson : NSObject
@property (nonatomic, strong) NSString *strongName;
@property (nonatomic, copy)   NSString *lcopyName;
@end

//MLPerson.m
#import "MLPerson.h"
@implementation MLPerson
- (NSString *)description
{
   return [NSString stringWithFormat:@"strong Name :%@  -- copy Name: %@", _strongName,_lcopyName];
}
@end

如上所示厘擂,在 MLPerson 類中添加了兩個(gè)屬性: strongNamelcopyName昆淡, 這兩個(gè)屬性分別用 strong 和 copy 修飾, 為了查看方便刽严,重寫了 description 方法昂灵。


看官卻道,咦舞萄?這廝為何 copy 修飾的不起一個(gè) copyName 的名字呢眨补?
欲知詳細(xì),且看 Tip2


在 main.m 中測(cè)試代碼如下:

#import <Foundation/Foundation.h>
#import "MLPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MLPerson *jack = [MLPerson new];
        NSMutableString *name = [NSMutableString stringWithString:@"Hello world"];
        jack.strongName = name;
        jack.copyName = name;
        NSLog(@"%@",jack);
        // 修改name
        [name appendString:@" one"];
        NSLog(@"%@",jack);
        
    }
    return 0;
}

控制輸出結(jié)果:

strong Name :Hello world  -- copy Name: Hello world
strong Name :Hello world one  -- copy Name: Hello world
Program ended with exit code: 0

在 jack 這個(gè)對(duì)象生成之后倒脓,分別為其兩個(gè)屬性賦值撑螺,這個(gè)值是一個(gè)可變字符串,因 NSMutableString 是 NSString 的子類崎弃,所以這么做是沒有問題的甘晤。賦值結(jié)束之后,將 name 后面追加了 " one" 這一個(gè)字符串饲做,現(xiàn)在 name 的值是: "Hello world one"线婚。 按照面向?qū)ο蠓庋b思想而言,此時(shí)此刻 jack 的這兩個(gè)屬性都不應(yīng)該改變盆均,因?yàn)閷?duì)對(duì)象的修改最好是通過 setter 方法或者公開的方法進(jìn)行塞弊。通過控制臺(tái)打印的結(jié)果來看,strong 修飾的屬性是不能滿足要求的泪姨,而copy則是可以滿足要求的游沿。
所以,在開發(fā)的過程中肮砾,為了讓類的封裝性不被破壞诀黍,針對(duì) NSString 最好使用 copy 來進(jìn)行修飾,這樣的代碼會(huì)更清晰一些唇敞,也不容出錯(cuò)蔗草。

原因解釋:


圖 1-1

如圖 1-1 所示咒彤,當(dāng)執(zhí)行 jack.strongName = name 和 jack.lcopyName = name 的時(shí)候疆柔,實(shí)際上咒精,_strongName 指向的是 ① 的 "Hello world" 而 _lcopyName 指向的確實(shí)另一份內(nèi)容與 name 指向的位置一樣的 "Hello world", 后面代碼對(duì) name 的追加是修改的 ① 處的 "Hello world" ,結(jié)果一目了然旷档。
而對(duì)于不可變字符串而言模叙,則沒有什么區(qū)別。


Tip2: 聲明了一個(gè)屬性名字為 copyName 為什么編譯不通過鞋屈,如果就想使用這個(gè)名字該如何去做范咨?

在聲明屬性的時(shí)候,尤其是為了區(qū)分兩個(gè)屬性厂庇,經(jīng)常用寫成 newName渠啊、copyName 或者其他,但是往往 Xcode 編譯不通過权旷,并且報(bào)錯(cuò)替蛉,如圖 1-2 所示:

? Property follows Cocoa naming convention for returning 'owned' objects
圖 1-2

在開發(fā)者文檔《Memory Management Policy》中有這么一條內(nèi)存管理策略:

You own any object you create
You create an object using a method whose name begins with "alloc", "new", "copy", or "mutableCopy".
(for example, alloc, newObject, or mutableCopy).

上面這條規(guī)則說的是在 MRR(memory retain release)內(nèi)存管理下的一條規(guī)則。
聲明屬性為 copyName 拄氯,也就是會(huì)默認(rèn)產(chǎn)生 setCopyName 和 copyName 這兩個(gè)方法 setter 和 getter 方法, 然而根據(jù)內(nèi)存規(guī)則來說躲查,通過 newXXX 方法就是持有 newXXX 方法返回的對(duì)象,getter 方法并不是用來持有對(duì)象的译柏,這樣就造成了奇異镣煮,so 編譯器直接報(bào)錯(cuò)。
解決這個(gè)問題鄙麦,最簡(jiǎn)單的方法就是改名典唇,比如:

@property (nonatomic, copy) NSString *theCopyName;

當(dāng)然有句話就:就不信邪!
如果執(zhí)意要用這個(gè)名字的話胯府,可以修改編譯器默認(rèn)為創(chuàng)建的 getter 方法的名字:

@property (nonatomic, copy, getter = theCopyName) NSString *copyName;

這樣編譯也是通過的介衔。
還有一種方式就是使用 Function attribute 來修飾 getter 方法

@property (nonatomic, copy)   NSString *copyName ;
- (NSString *)copyName __attribute__((objc_method_family(none)));

上面這種寫法也是可以通過驗(yàn)證的。

當(dāng)然這里又有: __attribute__ 是什么的疑問了盟劫?且看 Tip3


Tip3: __attribute__ 是什么夜牡?

從上面可以看到,當(dāng)為 copyName 的 getter 方法添加了 attribute 后面這一段之后侣签,編譯器便不再報(bào)錯(cuò)塘装。盡管你不知道 attribute 是什么,卻仍然可以推斷出它讓編譯器忽略了對(duì)這個(gè)方法的內(nèi)存規(guī)則檢查即其為編譯器提供了上下文影所。
首先蹦肴,來看下 NSFoundation 框架中 NS_REQUIRES_SUPER 的使用:
NS_REQUIRES_SUPER 是在 NSObjCRuntime.h 中定義的預(yù)編譯指令,定義如下

#ifndef NS_REQUIRES_SUPER
#if __has_attribute(objc_requires_super)
#define NS_REQUIRES_SUPER __attribute__((objc_requires_super))
#else
#define NS_REQUIRES_SUPER
#endif
#endif

可以看到第三句中:

#define NS_REQUIRES_SUPER __attribute__((objc_requires_super))

和上面針對(duì)屬性的使用非常相似猴娩,這句話定義了之后阴幌,當(dāng)使用 NS_REQUIRES_SUPER 地方在預(yù)編譯時(shí)期就會(huì)被替換為 __attribute__((objc_requires_super)) (這一點(diǎn)在后面來進(jìn)行驗(yàn)證)
在 MLPerson 類中添加方法 - work :

//MLPerson.h
@interface MLPerson : NSObject

@property (nonatomic, strong) NSString *strongName;
@property (nonatomic, copy)   NSString *copyName ;
//@property (nonatomic, copy, getter=theCopyName)   NSString *copyName;
- (NSString *)copyName __attribute__((objc_method_family(none)));

- (void)work NS_REQUIRES_SUPER;
@end


//MLPerson.m
#import "MLPerson.h"

@implementation MLPerson

- (NSString *)description
{
    return [NSString stringWithFormat:@"strong Name :%@  -- copy Name: %@", _strongName,_copyName];
}


- (void)work {
    NSLog(@"MLPerson - work method");
}
@end

創(chuàng)建 MLStudent 類繼承自 MLPerson 類:

//MStudent.h
#import "MLPerson.h"
@interface MLStudent : MLPerson

@end

//MStudent.m
#import "MLStudent.h"

@implementation MLStudent
- (void)work {
    NSLog(@"MLStudent - work method");
}
@end

在為 MLStudent 類添加 work 方法的時(shí)候勺阐,可以看到如下警告信息,如圖 1-3:

圖 1-3

當(dāng)然如果不調(diào)用的話也是可以編譯通過的矛双,但是這里會(huì)彈出警告??讓寫此類的人知道此處應(yīng)該通過 [super work] 調(diào)用父類的方法渊抽。

  1. 通過觀察 * NS_REQUIRES_SUPER* 的使用,對(duì)先前 MLPerson 中 copyName 的 getter 方法修飾做出修改议忽,如下:
#if __has_attribute(objc_method_family)
#define ML_OBJC_METHOD_FAMILY_NONE __attribute__((objc_method_family(none)))
#else
#define ML_OBJC_METHOD_FAMILY_NONE
#endif

#import <Foundation/Foundation.h>

@interface MLPerson : NSObject

@property (nonatomic, strong) NSString *strongName;
@property (nonatomic, copy)   NSString *copyName ;
//@property (nonatomic, copy, getter=theCopyName)   NSString *copyName;
//- (NSString *)copyName __attribute__((objc_method_family(none)));

- (NSString *)copyName ML_OBJC_METHOD_FAMILY_NONE;

- (void)work NS_REQUIRES_SUPER;
@end

這里使用 ML_OBJC_METHOD_FAMILY_NONE 對(duì) __attribute__ 做了預(yù)定義處理

  1. 驗(yàn)證預(yù)處理階段的替換
//main.m
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "MLPerson.h"
#import "MLStudent.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
 
        MLPerson *jack = [MLPerson new];
        
        NSMutableString *name = [NSMutableString stringWithString:@"Hello world"];
        
        jack.strongName = name;
        jack.copyName = name;
        NSLog(@"%@",jack);
        [name appendString:@" one"];
        NSLog(@"%@",jack);
        
        
        NSString *newName = @"one world";
        jack.strongName = newName;
        jack.copyName = newName;
        NSLog(@"%@",jack);
        newName = @"xxx";
        NSLog(@"%@",jack);
        
        NSString *str = @"Hello world";   
        
        MLStudent *liLei = [MLStudent new];
        [liLei work];
    }
    return 0;
}

打開終端懒闷,進(jìn)入到項(xiàng)目目錄,與 main.m 在同一層級(jí):

 $tree
.
├── MLPerson.h
├── MLPerson.m
├── MLStudent.h
├── MLStudent.m
└── main.m

0 directories, 5 files
$clang -E -fmodules main.m -o main # 對(duì) main.m 執(zhí)行預(yù)處理操作栈幸,輸出文件名為main
$tree
.
├── MLPerson.h
├── MLPerson.m
├── MLStudent.h
├── MLStudent.m
├── main # 目標(biāo)文件已經(jīng)生成
└── main.m
$cat main
# 1 "main.m"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 343 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.m" 2








@import Foundation; /* clang -E: implicit import for "/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" */
@import AppKit; /* clang -E: implicit import for "/System/Library/Frameworks/AppKit.framework/Headers/AppKit.h" */
# 1 "./MLPerson.h" 1
# 18 "./MLPerson.h"
@import Foundation; /* clang -E: implicit import for "/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" */

@interface MLPerson : NSObject

@property (nonatomic, strong) NSString *strongName;
@property (nonatomic, copy) NSString *copyName ;


- (NSString *)copyName __attribute__((objc_method_family(none)));


- (void)work __attribute__((objc_requires_super));
@end
# 12 "main.m" 2
# 1 "./MLStudent.h" 1
# 11 "./MLStudent.h"
@interface MLStudent : MLPerson

@end
# 13 "main.m" 2

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        MLPerson *jack = [MLPerson new];

        NSMutableString *name = [NSMutableString stringWithString:@"Hello world"];

        jack.strongName = name;
        jack.copyName = name;
        NSLog(@"%@",jack);
        [name appendString:@" one"];
        NSLog(@"%@",jack);


        NSString *newName = @"one world";
        jack.strongName = newName;
        jack.copyName = newName;
        NSLog(@"%@",jack);
        newName = @"xxx";
        NSLog(@"%@",jack);

        NSString *str = @"Hello world";

        MLStudent *liLei = [MLStudent new];
        [liLei work];
    }
    return 0;
}

從 #18 的地方截取看到

@import Foundation; /* clang -E: implicit import for "/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" */

@interface MLPerson : NSObject

@property (nonatomic, strong) NSString *strongName;
@property (nonatomic, copy) NSString *copyName ;


- (NSString *)copyName __attribute__((objc_method_family(none)));


- (void)work __attribute__((objc_requires_super));
@end

這就是預(yù)處理之后 MLPerson 中的內(nèi)容愤估,我們可以看到注釋已經(jīng)去除,并且宏也已經(jīng)被替換速址。
上面通過 clang -E -fmodules main.m -o main 生成了預(yù)處理之后的 main 文件
當(dāng)然通過:gcc -E -Foundation main.m -o main 也是可以做到這一步的玩焰,只不過目標(biāo)文件中導(dǎo)入的 Foudation 會(huì)被展開。

至此芍锚,我們可以看到 __attribute__ 是為編譯器提供上下問的一個(gè)工具或者方式昔园,在 Cocoa 中早有使用,目前先了解到此處闹炉,后面做專門分析蒿赢。


end


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渣触,隨后出現(xiàn)的幾起案子羡棵,更是在濱河造成了極大的恐慌,老刑警劉巖嗅钻,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皂冰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡养篓,警方通過查閱死者的電腦和手機(jī)秃流,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柳弄,“玉大人舶胀,你說我怎么就攤上這事”套ⅲ” “怎么了嚣伐?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萍丐。 經(jīng)常有香客問我轩端,道長(zhǎng),這世上最難降的妖魔是什么逝变? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任基茵,我火速辦了婚禮奋构,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拱层。我一直安慰自己弥臼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布舱呻。 她就那樣靜靜地躺著醋火,像睡著了一般悠汽。 火紅的嫁衣襯著肌膚如雪箱吕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天柿冲,我揣著相機(jī)與錄音茬高,去河邊找鬼。 笑死假抄,一個(gè)胖子當(dāng)著我的面吹牛怎栽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宿饱,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼熏瞄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了谬以?” 一聲冷哼從身側(cè)響起强饮,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎为黎,沒想到半個(gè)月后邮丰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铭乾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年剪廉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炕檩。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡斗蒋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出笛质,到底是詐尸還是另有隱情泉沾,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布经瓷,位于F島的核電站爆哑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舆吮。R本人自食惡果不足惜揭朝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一队贱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧潭袱,春花似錦柱嫌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至彤悔,卻和暖如春嘉抓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晕窑。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工抑片, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杨赤。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓敞斋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疾牲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子植捎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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

  • 明天我們就要比賽了我心里邊非常的激動(dòng)我的東西們都準(zhǔn)備好
    付夢(mèng)緣閱讀 128評(píng)論 0 0