@property in Category

原文鏈接
轉(zhuǎn)載注明出處

前言

我們是可以在Category中添加屬性的忙厌,就像以下代碼

/** FaiChouView.h */
#import <UIKit/UIKit.h>

@interface FaiChouView : UIView

@end

@interface FaiChouView (fcHeight)
@property CGFloat fcHeight;
@end

/** FaiChouView.m */
#import "FaiChouView.h"

@implementation FaiChouView
@end
@implementation FaiChouView (fcHeight)

- (CGFloat)fcHeight {
    return self.fcHeight;
}
- (void)setFcHeight:(CGFloat)fcHeight {
    self.fcHeight = fcHeight;
}
@end

/** main.m */
#import "FaiChouView.h"
int main(int argc, char * argv[]) {
    @autoreleasepool {
        FaiChouView *fcView = [[FaiChouView alloc] init];
        fcView.fcHeight = 20.; 
        NSLog(@"%f", fcView.fcHeight);
    }
}

毫無(wú)疑問(wèn)逢净,它會(huì)崩潰的。問(wèn)題出在哪爹土?如何修改踩身?一步一步來(lái)胀茵。

到底可不可以添加屬性到Category挟阻?

我們可以在蘋(píng)果官方文檔中找到以下說(shuō)明:

Categories can be used to declare either instance methods or class methods but are not usually suitable for declaring additional properties. It’s valid syntax to include a property declaration in a category interface, but it’s not possible to declare an additional instance variable in a category. This means the compiler won’t synthesize any instance variable, nor will it synthesize any property accessor methods. You can write your own accessor methods in the category implementation, but you won’t be able to keep track of a value for that property unless it’s already stored by the original class.

文檔中明確指出It’s valid syntax to include a property declaration in a category interface附鸽,我們可以在接口文件中聲明屬性脱拼,但是編譯器不會(huì)自動(dòng)合成實(shí)例變量(Ivars)和存取方法坷备。
我們應(yīng)該自己實(shí)現(xiàn)存取方法。

我們的代碼到底錯(cuò)在什么地方击你?

won’t be able to keep track of a value for that property

我們的代碼return self.fcHeight;已經(jīng)無(wú)法無(wú)天了谎柄。

如何修改代碼讓其正常獲取view的高度?

@implementation FaiChouView (fcHeight)

- (CGFloat)fcHeight {
    // return self.fcHeight;
    
    return self.frame.size.height;
}
- (void)setFcHeight:(CGFloat)fcHeight {
    
    // self.fcHeight = fcHeight;
    
    CGRect newframe = self.frame;
    newframe.size.height = fcHeight;
    self.frame = newframe;
}

@end

官方文檔的說(shuō)明鸿摇,我們?cè)?code>Category中的屬性是不能夠保存其值的劈猿,但是我們可以自定義存取方法拙吉,fcHeight返回的是view本身的高度, setter方法也不將值賦值給fcHeight揪荣,而間接的給view.frame,這樣在main.m中的fcView.fcHeight = 20.;只是借助了fcHeight的存取方法給view本身的賦值佛舱。

通過(guò)以上,我們驗(yàn)證了那句古話

不能在Category中添加實(shí)例變量

學(xué)而不思則罔请祖,思而不學(xué)則殆

只要挖到runtime,任何東西都是一目了然的肆捕。
有些博客看了一遍看不懂,那就多看幾遍慎陵,博客講的只是一個(gè)方面,上面有許多許多知識(shí)點(diǎn)蒙幻,這一面映射出來(lái)的所有點(diǎn)沒(méi)有必要全部掌握,發(fā)現(xiàn)問(wèn)題的關(guān)鍵邮破,帶著問(wèn)題考慮仆救,總會(huì)學(xué)到很多知識(shí)的。

少談些主義彤蔽,多研究些問(wèn)題。 ———— 胡適

objc所有類(lèi)和對(duì)象都是c結(jié)構(gòu)體顿痪,category當(dāng)然也一樣,下面是runtime中Category的結(jié)構(gòu):

struct _category_t {
    const char *name; // 類(lèi)名
    struct _class_t *cls; //
    const struct _method_list_t *instance_methods; // 實(shí)例方法 -
    const struct _method_list_t *class_methods; // 類(lèi)方法 +
    const struct _protocol_list_t *protocols; // 
    const struct _prop_list_t *properties; //
};

properties這個(gè)category所有的property征懈,這也是category里面可以定義屬性的原因,不過(guò)這個(gè)property不會(huì)合成實(shí)例變量和存取方法卖哎。

一個(gè)普通的屬性(fcTestProperty)删性,經(jīng)過(guò)編譯器編譯過(guò)后,會(huì)添加以下:

  1. _ivar_list_t中添加了_fcTestProperty變量
  2. _method_list_t中添加了fcTestPropertysetFcTestProperty兩個(gè)方法
  3. _prop_list_t中添加了fcTestProperty這個(gè)property

一個(gè)Category中的屬性(fcTestProperty),經(jīng)過(guò)編譯器編譯后蹬挺,只會(huì)在_prop_list_t中增加fcTestProperty這個(gè)屬性。

如何在Category中添加實(shí)例變量呢巴帮?

MJRefresh中我們可以找到以下代碼(摘要):

/** UIScrollView+MJRefresh.h */

#import <UIKit/UIKit.h>
#import "MJRefreshConst.h"

@class MJRefreshHeader, MJRefreshFooter;

@interface UIScrollView (MJRefresh)
/** 下拉刷新控件 */
@property (strong, nonatomic) MJRefreshHeader *mj_header;

...

@end


/** UIScrollView+MJRefresh.m */

#pragma mark - header
static const char MJRefreshHeaderKey = '\0';
- (void)setMj_header:(MJRefreshHeader *)mj_header
{
    if (mj_header != self.mj_header) {
        // 刪除舊的,添加新的
        [self.mj_header removeFromSuperview];
        [self insertSubview:mj_header atIndex:0];
        
        // 存儲(chǔ)新的
        [self willChangeValueForKey:@"mj_header"]; // KVO
        objc_setAssociatedObject(self, &MJRefreshHeaderKey,
                                 mj_header, OBJC_ASSOCIATION_ASSIGN);
        [self didChangeValueForKey:@"mj_header"]; // KVO
    }
}

- (MJRefreshHeader *)mj_header
{
    return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
}

...

他是通過(guò)runtime中的關(guān)聯(lián)對(duì)象實(shí)現(xiàn)的发乔,關(guān)于Associated Objects可以學(xué)習(xí)NSHipster的魔鬼的交易這一篇。

最后我們的代碼調(diào)整為:

/** FaiChouView.m */
#import "FaiChouView.h"
#import <objc/runtime.h>

@implementation FaiChouView
@end

@implementation FaiChouView (fcHeight)
@dynamic fcHeight;
- (CGFloat)fcHeight {
    // return self.fcHeight;
    
    // return self.frame.size.height;
    return [objc_getAssociatedObject(self, @selector(fcHeight)) floatValue];
}
- (void)setFcHeight:(CGFloat)fcHeightNew {
    
    // self.fcHeight = fcHeight;
    
//    CGRect newframe = self.frame;
//    newframe.size.height = fcHeight;
//    self.frame = newframe;
    NSNumber *fcHeightFloatNumber = @(fcHeightNew);
    objc_setAssociatedObject(self, @selector(fcHeight), fcHeightFloatNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

objc_setAssociatedObjectobjc_getAssociatedObject方法綁定的實(shí)例變量與一個(gè)普通的實(shí)例變量完全是兩碼事起愈。

參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末译仗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纵菌,更是在濱河造成了極大的恐慌,老刑警劉巖笛辟,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件序苏,死亡現(xiàn)場(chǎng)離奇詭異手幢,居然都是意外死亡忱详,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)监透,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)航唆,“玉大人胀蛮,你說(shuō)我怎么就攤上這事佛点±璞龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵阅虫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我米碰,道長(zhǎng)窝革,這世上最難降的妖魔是什么吕座? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮漆诽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘厢拭。我一直安慰自己,他們只是感情好供鸠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布陨闹。 她就那樣靜靜地躺著,像睡著了一般正林。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上觅廓,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音帖蔓,去河邊找鬼。 笑死塑娇,一個(gè)胖子當(dāng)著我的面吹牛劫侧,可吹牛的內(nèi)容都是我干的埋酬。 我是一名探鬼主播烧栋,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼审姓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起扎筒,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嗜桌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體症脂,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年壶唤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棕所。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡琳省,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出针贬,到底是詐尸還是另有隱情,我是刑警寧澤桦他,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站圆仔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坪郭。R本人自食惡果不足惜脉幢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫌松。 院中可真熱鬧,春花似錦豆瘫、人聲如沸菊值。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)磅崭。三九已至,卻和暖如春砸喻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背割岛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工犯助, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剂买。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瞬哼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坐慰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉讨越,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,690評(píng)論 0 9
  • 1.切換到其它分區(qū)。 使用D:進(jìn)入到D盤(pán)把跨,如圖: 2.進(jìn)入當(dāng)前分區(qū)的目錄。 使用cd D:\pythoncode進(jìn)...
    知孺閱讀 441評(píng)論 0 0
  • 木子睜開(kāi)朦朧的雙眼耸别,暖陽(yáng)透過(guò)窗簾灑在房間,泛著粉紅色的光暈秀姐。 側(cè)過(guò)身,木子看著身邊的熟睡的男人省有,心里忽然一陣刺痛谴麦,...
    冷色中的一抹暖色調(diào)閱讀 2,122評(píng)論 0 3
  • 有了女兒之后恤磷,很關(guān)注教育問(wèn)題。今天的剽悍晨讀提煉了《高效能人士的七個(gè)習(xí)慣》的前三個(gè)要點(diǎn)扫步,想借此機(jī)會(huì)跟各位教育界或感...
    魯曼啊漫閱讀 548評(píng)論 0 1
  • 鵬城有古寺,內(nèi)構(gòu)金輝煌河胎。 凡人心欲動(dòng),時(shí)時(shí)待前往仿粹。 奈何塵世中,身處俗事間吭历。 看青燈古佛,聽(tīng)暮鼓晨鐘晌区。 參禪得大智...
    無(wú)敵鄭大大閱讀 715評(píng)論 2 2