Runtime之@dynamic關(guān)鍵字

講述@dynamic之前,需要了解幾個(gè)名詞。最后再重點(diǎn)介紹@dynamic的用法

  • @property
    • 原子性
    • 存取控制
    • 內(nèi)存管理
  • @synthesize

@property

使用 @property 聲明的屬性腕扶,可以方便快捷的為實(shí)例變量創(chuàng)建存取器(getter 和 setter)。默認(rèn)以下劃線 _ 開頭。比如

@interface Person : NSObject
@property(nonatomic, readwrite, strong) NSString *name;
-(void)sayHello;
@end
    
@implementation Person
@synthesize name = _name;//默認(rèn)情況懒鉴,可以不寫犬性。除非想通過其他的單詞(非_name)來使用
-(void)sayHello{
    NSLog(@"%@ says hello",_name);
}
@end

此時(shí)瞻离,我們可以通過點(diǎn)訪問符 . 來給對(duì)象的name屬性進(jìn)行存取。

    //設(shè)置
    Person* person1 = [[Person alloc] init];
    person1.name = @"zhangsan";
    [person1 sayHello];
    //獲取
    NSLog(@"person1's name is %@",[person1.name sayBye]);
原子性

? 在上述例子中乒裆,我們看到了聲明語句括號(hào)內(nèi)的nonatomic關(guān)鍵字套利。具體原子性包含兩個(gè)關(guān)鍵字:

  • atomic
    • 默認(rèn)。原子的鹤耍,意味著只有一個(gè)線程訪問實(shí)例變量(的getter和setter)肉迫。
    • 線程安全,至少在當(dāng)前的存取器上是安全的
    • 影響效率稿黄,使用較少
  • nonatomic
    • 非原子的喊衫,可以同時(shí)被多個(gè)線程訪問
    • 效率高
    • 多線程下不安全,使用較多
存取控制

? 在上述例子中抛猖,我們看到了聲明語句括號(hào)內(nèi)的readwrite關(guān)鍵字格侯。表示屬性的存取特征。存取控制具體包含以下幾個(gè):

  • readwrite
    • 默認(rèn)屬性财著,系統(tǒng)會(huì)自動(dòng)給屬性生成getter和setter存取器
  • readonly
    • 只讀屬性联四,只會(huì)生成getter;不能通過點(diǎn)運(yùn)算符(setter)給屬性賦值
內(nèi)存管理

? 在上述例子中撑教,我們看到了聲明語句括號(hào)內(nèi)的strong關(guān)鍵字朝墩。表示屬性內(nèi)存管理的關(guān)鍵字,有以下幾個(gè):

  • assign
    • 默認(rèn)伟姐,適用于值類型收苏。如int、NSInteger愤兵、CGFloat鹿霸、float等。
  • retain
    • 在setter方法中秆乳,會(huì)對(duì)傳入的對(duì)象的引用計(jì)數(shù)器加1(理解ARC)懦鼠。
  • strong
    • strong是iOS引入ARC之后的關(guān)鍵字,是retain的一個(gè)可選的替代屹堰。
  • weak
    • 與retain相比肛冶,對(duì)引入對(duì)象的引用計(jì)數(shù)器,不進(jìn)行加1操作扯键。經(jīng)常用于delegate等睦袖。
  • copy
    • 與strong類似,區(qū)別是copy是創(chuàng)建了一個(gè)新對(duì)象荣刑。通常NSString類屬性會(huì)使用copy馅笙。

@synthesize

? @synthesize默認(rèn)會(huì)給屬性添加一個(gè)別名伦乔,比如上個(gè)例子中的_name 。默認(rèn)情況下延蟹,對(duì)于@property的變量都會(huì)生成相關(guān)別名和存取器

@dynamic

? 如果某屬性已實(shí)現(xiàn)了自己的getter和setter(當(dāng)然對(duì)于 readonly 的屬性只有 getter )评矩,可以通過@dynamic關(guān)鍵字來阻止自動(dòng)生成getter和setter的覆蓋。@dynamic關(guān)鍵字有兩個(gè)作用:

  • 讓編譯器不要?jiǎng)?chuàng)建實(shí)現(xiàn)屬性所用的實(shí)例變量阱飘;
  • 讓編譯器不要?jiǎng)?chuàng)建該屬性的get和setter方法斥杜。

編譯時(shí)沒問題,在運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法沥匈,這就是所謂的動(dòng)態(tài)綁定蔗喂。

假如一個(gè)屬性被聲明為@dynamic var,然而沒有提供對(duì)應(yīng)的getter和setter方法高帖,編譯的時(shí)候不會(huì)報(bào)錯(cuò)缰儿,但是當(dāng)程序運(yùn)行到 instance.var = xxx 時(shí),由于缺少setter方法會(huì)導(dǎo)致程序崩潰


@dynamic的使用

這里介紹三種

  • 通過私有變量來實(shí)現(xiàn)@dynamic
  • Category擴(kuò)展
  • 動(dòng)態(tài)解析和函數(shù)重簽名

通過私有變量來實(shí)現(xiàn)@dynamic

通過私有變量來實(shí)現(xiàn)@dynamic散址,可以達(dá)到隱藏某些信息的目的乖阵。
例如DemoClass1中有個(gè)dynamicVar屬性,我們將其設(shè)置為@dynamic预麸,在程序運(yùn)行期間瞪浸,我們想通過他來存取某個(gè)私有(對(duì)象的)屬性,簡(jiǎn)單起見吏祸,我們將對(duì)應(yīng)的私有屬性命名為privateVar对蒲。
.h 文件:

/***
 * 用來展示通過私有變量來實(shí)現(xiàn)@dynamic
 * 用于隱藏某些信息
 ***/
@interface DemoClass1 : NSObject
@property (nonatomic , strong) NSString* dynamicVar;
@end

.m 文件:

@interface DemoClass1()
@property (nonatomic, strong) NSString* privateVar;
@end

@implementation DemoClass1
@dynamic dynamicVar;
@synthesize privateVar = _privateVar;

- (void)setDynamicVar:(NSString *)dynamicVar{
    self.privateVar = dynamicVar;
}
-(NSString*)dynamicVar{
    return self.privateVar;
}

-(void)setPrivateVar:(NSString *)privateVar{
    _privateVar = privateVar;
}
-(NSString*)privateVar{
    if (!_privateVar) {
        _privateVar = @"This is private var";
    }
    return _privateVar;
}

方法的調(diào)用,我們通過調(diào)用dynamicVar的getter和setter贡翘,然后看下對(duì)應(yīng)值

    DemoClass1* demo1 = [[DemoClass1 alloc] init];
    NSLog(@"Before - DemoClass1's dynamicVar is :%@", demo1.dynamicVar);
    demo1.dynamicVar = @"Set dynamicVar";
    NSLog(@"After - DemoClass1's dynamicVar is :%@", demo1.dynamicVar);

輸出信息如下:

2019-01-25 09:19:13.253753+0800 property_dynamic[18448:7311996] Before - DemoClass1's dynamicVar is :This is private var
2019-01-25 09:19:13.253893+0800 property_dynamic[18448:7311996] After - DemoClass1's dynamicVar is :Set dynamicVar

我們可以看到蹈矮,當(dāng)我們?cè)L問dynamicVar的getter方法時(shí),最終展示的私有變量privateVar鸣驱。而訪問setter泛鸟,最終設(shè)置的也是privateVar。
通過@dynamic關(guān)鍵字最終實(shí)現(xiàn)了將私有信息暴漏的目的踊东。

Category擴(kuò)展

另一個(gè)較常用的用法就是Category谈况,例如,我們想對(duì)UILabel進(jìn)行行間距和列間距的設(shè)置递胧,我們想通過兩個(gè)屬性來設(shè)置
.h 文件:

@interface UILabel (Demo2)
//行間距
@property (nonatomic, assign) CGFloat ltx_lineSpace;
//字間距
@property (nonatomic, assign) CGFloat ltx_wordSpace;
@end

.m 文件:

#import "UILabel+Demo2.h"
#import <objc/runtime.h>

NSString const *ltx_label_lineSpace = @"ltx_label_lineSpace";
NSString const *ltx_label_wordSpace = @"ltx_label_wordSpace";
@implementation UILabel (Demo2)
@dynamic ltx_lineSpace;
@dynamic ltx_wordSpace;

-(void)ltx_updateLabelLineSpace{
    NSString* text = self.text;
    NSInteger lineSpace = self.ltx_lineSpace;
    NSInteger wordSpace = self.ltx_wordSpace;
    if ([text length] > 0) {
        NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
        if (lineSpace > 0) {
            NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
            [paragraphStyle setLineSpacing:lineSpace];
            [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [text length])];
        }
        if (wordSpace > 0) {
            [attributedString addAttribute:NSKernAttributeName value:[NSNumber numberWithFloat:wordSpace] range:NSMakeRange(0, [text length])];
        }
        self.attributedText = attributedString;
    }
}


#pragma mark - Getter && Setter
-(void)setLtx_lineSpace:(CGFloat)ltx_lineSpace{
    NSNumber* number = [NSNumber numberWithFloat:ltx_lineSpace];
    objc_setAssociatedObject(self, &ltx_label_lineSpace, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self ltx_updateLabelLineSpace];
}
-(CGFloat)ltx_lineSpace{
    NSNumber* number =  objc_getAssociatedObject(self, &ltx_label_lineSpace);
    return [number floatValue];
}

-(void)setLtx_wordSpace:(CGFloat)ltx_wordSpace{
    NSNumber* number = [NSNumber numberWithFloat:ltx_wordSpace];
    objc_setAssociatedObject(self, &ltx_label_wordSpace, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self ltx_updateLabelLineSpace];
}
-(CGFloat)ltx_wordSpace{
    NSNumber* number =  objc_getAssociatedObject(self, &ltx_label_wordSpace);
    return [number floatValue];
}

方法的調(diào)用,我們通過調(diào)用ltx_lineSpaceltx_wordSpace 來對(duì)labe的列間距和字間距進(jìn)行設(shè)置

    //設(shè)置UILabel的行間距和字間距
    self.label.ltx_lineSpace = 8;
    self.label.ltx_wordSpace = 4;

效果如下

動(dòng)態(tài)修改UILabel的行間距和字間距.png

更多擴(kuò)展赡茸,請(qǐng)移步參照:https://github.com/liangtongdev/LTxCategories

動(dòng)態(tài)解析和函數(shù)重簽名

即通過對(duì)@dynamic動(dòng)態(tài)屬性的getter和setter進(jìn)行以下操作缎脾。

  • 動(dòng)態(tài)解析
    • 重寫NSObject的方法 + (BOOL)resolveInstanceMethod:(SEL)sel
    • 利用class_addMethod方法,將其他方法的實(shí)現(xiàn)添加給屬性的getter和setter方法占卧。
  • 函數(shù)重簽名
    • 消息轉(zhuǎn)發(fā)
      • 重寫NSObject的方法-(id)forwardingTargetForSelector:(SEL)aSelector
      • 在方法內(nèi)部遗菠,對(duì)動(dòng)態(tài)屬性的getter和setter方法進(jìn)行轉(zhuǎn)發(fā)联喘,由其他對(duì)象來實(shí)現(xiàn)具體細(xì)節(jié)。
    • 方法重簽名
      • 重寫NSObject的方法 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
      • 在方法內(nèi)辙纬,返回動(dòng)態(tài)屬性的getter和setter方法的新的方法簽名
      • 重寫NSObject的方法- (void)forwardInvocation:(NSInvocation *)anInvocation
      • 在方法內(nèi)部豁遭,對(duì)重新簽名的方法的target進(jìn)行賦值,并喚醒

具體細(xì)節(jié)贺拣,請(qǐng)移步參照文章:Runtime之objc_msgSend執(zhí)行流程


Demo

https://github.com/liangtongdev/Demo-property_dynamic

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蓖谢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子譬涡,更是在濱河造成了極大的恐慌闪幽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涡匀,死亡現(xiàn)場(chǎng)離奇詭異盯腌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)陨瘩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門腕够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舌劳,你說我怎么就攤上這事帚湘。” “怎么了蒿囤?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵客们,是天一觀的道長。 經(jīng)常有香客問我材诽,道長底挫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任脸侥,我火速辦了婚禮建邓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘睁枕。我一直安慰自己官边,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布外遇。 她就那樣靜靜地躺著注簿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跳仿。 梳的紋絲不亂的頭發(fā)上诡渴,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音菲语,去河邊找鬼妄辩。 笑死惑灵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的眼耀。 我是一名探鬼主播英支,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哮伟!你這毒婦竟也來了干花?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤澈吨,失蹤者是張志新(化名)和其女友劉穎把敢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谅辣,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡修赞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桑阶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柏副。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚣录,靈堂內(nèi)的尸體忽然破棺而出割择,到底是詐尸還是另有隱情,我是刑警寧澤萎河,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布荔泳,位于F島的核電站,受9級(jí)特大地震影響虐杯,放射性物質(zhì)發(fā)生泄漏玛歌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一擎椰、第九天 我趴在偏房一處隱蔽的房頂上張望支子。 院中可真熱鬧,春花似錦达舒、人聲如沸值朋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昨登。三九已至,卻和暖如春贯底,著一層夾襖步出監(jiān)牢的瞬間篙骡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糯俗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓睦擂,卻偏偏與公主長得像得湘,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子顿仇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,101評(píng)論 1 32
  • 1. load 與 initialize load:是當(dāng)類或分類被添加到 Objective-C runtime ...
    杰克道長閱讀 1,359評(píng)論 0 3
  • 橙子是一枚初三的學(xué)生淘正,每天都過的懶懶散散,眼看就要中考臼闻,無奈父母威逼利誘開始了補(bǔ)課生涯鸿吆,補(bǔ)課老師是父親大人的朋...
    清淺未央閱讀 389評(píng)論 2 0
  • 當(dāng)某筆交易被推動(dòng)時(shí)惩淳,除了所涉及的球員,隊(duì)友也會(huì)或多或少受到影響乓搬。這其中快船送出格里芬并換回布拉德利這很可能就會(huì)導(dǎo)致...
    zoneball閱讀 258評(píng)論 0 0
  • 01 達(dá)·芬奇開掛的一生 說起達(dá)·芬奇思犁,大家肯定都不陌生。他是文藝復(fù)興時(shí)期的一個(gè)重要人物谭跨。也許更多的人對(duì)達(dá)·芬奇的...
    蝸牛等花開閱讀 5,021評(píng)論 2 1