一:首先介紹Category好處:
1局雄、可以將類的實現(xiàn)分散到多個不同文件或多個不同框架中,方便代碼管理活尊。也可以對框架提供類的擴(kuò)展(因為框架類沒有源碼,不能修改)漏益。
2蛹锰、創(chuàng)建對私有方法的前向引用:如果其他類中的方法未實現(xiàn),在你訪問其他類的私有方法時編譯器報錯這時使用類別绰疤,在類別中聲明這些方法(不必提供方法實現(xiàn))铜犬,編譯器就不會再產(chǎn)生警告
3、向?qū)ο筇砑臃钦絽f(xié)議:創(chuàng)建一個NSObject的類別稱為“創(chuàng)建一個非正式協(xié)議”轻庆,因為可以作為任何類的委托對象使用癣猾。有兩個方面的局限性: (1)無法向類中添加新的實例變量,類別沒有位置容納實例變量余爆。(2)名稱沖突煎谍,即當(dāng)類別中的方法與原始類方法名稱沖突時,類別具有更高的優(yōu)先級龙屉。類別方法將完全取代初始方法從而無法再使用初始方法。這個類似于方法的重載,但是這里是直接覆蓋了原方法转捕。
二:Category只能添加方法作岖,而不能添加成員變量或?qū)傩?/h3>
我們知道在一個類中用@property聲明屬性,編譯器會自動幫我們生成成員變量和setter/getter五芝,但分類的指針結(jié)構(gòu)體中痘儡,根本沒有屬性列表。所以在分類中用@property聲明屬性枢步,既無法生成成員變量也無法生成setter/getter沉删。
因此:我們可以用@property聲明屬性,編譯會通過醉途,但run之后就會崩潰矾瑰。
三:如何添加?
既然報錯的根本原因是使用了系統(tǒng)沒有生成的setter/getter方法隘擎,可不可以在手動添加setter/getter來避免崩潰殴穴,完成調(diào)用呢?
其實是可以的货葬。由于OC是動態(tài)語言采幌,方法真正的實現(xiàn)是通過runtime完成的,雖然系統(tǒng)不給我們生成setter/getter震桶,但我們可以通過runtime手動添加setter/getter方法休傍。
(1)首先有一個類
.h文件
#import@interface Person : NSObject
@property (assign, nonatomic) NSInteger age;
-(void)run;
@end
.m文件
#import "Person.h"
@implementation Person
-(void)run{
NSLog(@"%s",__func__);
}
@end
(2)添加類別
.h文件
#import "Person.h"
@interface Person (PersonExtention)
@property (copy, nonatomic) NSString *name;
-(void)saySex;
@end
.m文件
#import "Person+PersonExtention.h"
#import <objc/runtime.h>
@implementation Person (PersonExtention)
//定義常量 必須是C語言字符串
static char *PersonNameKey = "PersonNameKey";
-(void)setName:(NSString *)name{
/*
OBJC_ASSOCIATION_ASSIGN; //assign策略
OBJC_ASSOCIATION_COPY_NONATOMIC; //copy策略
OBJC_ASSOCIATION_RETAIN_NONATOMIC; // retain策略
OBJC_ASSOCIATION_RETAIN;
OBJC_ASSOCIATION_COPY;
*/
/*
* id object 給哪個對象的屬性賦值
const void *key 屬性對應(yīng)的key
id value 設(shè)置屬性值為value
objc_AssociationPolicy policy 使用的策略,是一個枚舉值蹲姐,和copy磨取,retain,assign是一樣的淤堵,手機(jī)開發(fā)一般都選擇NONATOMIC
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
*/
objc_setAssociatedObject(self, PersonNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, PersonNameKey);
}
-(void)saySex{
NSLog(@"%s----%@",__func__,self);
}
@end
四:iOS分類不能添加屬性原因探索寝衫,
請參考:http://www.reibang.com/p/935142af6a47
五:疑問
有時候我們會看到如下現(xiàn)象:
這是為什么呢?
@property (nonatomic, assign) CGFloat x;
這里添加的屬性拐邪,實現(xiàn)我們在外部的調(diào)用慰毅,其實在這種情況下是不會自動生成實例變量的,我們只是通過重寫setter和getter方法來對self.frame
進(jìn)行操作扎阶,并不是針對的我們聲明的比如x
汹胃,我們使用的時候getter
方法中返回的是 self.frame.origin.x
六:下面解釋一下屬性和成員變量的區(qū)別:
@property (nonatomic, strong) UIButton *myButton;
我們聲明了一個屬性,因為現(xiàn)在我們用的編譯器已經(jīng)是LLVM了,所以不再需要為屬性聲明實例變量了东臀。如果LLVM發(fā)現(xiàn)一個沒有匹配實例變量的屬性着饥,它將為你生成以下劃線開頭的實例變量_myButton
,不需要自己手動再去寫實例變量惰赋。而且也不需要在.m文件中寫@synthesize myButton
宰掉;也會自動為你生成setter
呵哨,getter
方法。@synthesize
的作用就是讓編譯器為你自動生成setter與getter方法轨奄。那么在.m文件中可以直接的使用_myButton
實例變量孟害,也可以通過屬性self.myButton
兩者都是一樣的。
注意:
這里的self.myButton其實是調(diào)用的myButton屬性的getter/setter方法挪拟。
假如在Objective-C中我們這樣添加屬性聲明
@interface MyViewController:UIViewController
{
NSString *name;
}
@end
.m文件中挨务,self.name
這樣的表達(dá)式是錯誤的。Xcode會提示你使用->,改成self->name
就可以了玉组。因為oc中點表達(dá)式是表示調(diào)用方法谎柄,而上面的代碼中沒有name
這個方法。
(1)Objective-C中的點語法說明:
如果點表達(dá)式出現(xiàn)在 "=" 左邊惯雳,該屬性名稱的setter
方法將被調(diào)用朝巫。如果點表達(dá)式出現(xiàn)在右邊,該屬性名稱的getter
方法將被調(diào)用吨凑。所以在oc中點表達(dá)式其實就是調(diào)用對象的setter
和getter
方法的一種快捷方式捍歪。
@synthesize
還有一個作用,可以指定與屬性對應(yīng)的實例變量鸵钝,例如@synthesize myButton = xxxx糙臼;
那么self.myButton
其實是操作的實例變量xxxx
,而不是_myButton
了恩商。
在實際項目中,我們一般在.m中這樣寫:@synthesize myButton;
這樣寫了之后变逃,那么編譯器會自動生成myButton
的實例變量,以及相應(yīng)的getter
和setter
方法怠堪。注意:_myButton
這個實例變量是不存在的揽乱,因為自動生成的實例變量為myButton
而不是_myButton
,所以現(xiàn)在@synthesize
的作用就相當(dāng)于指定實例變量粟矿;
如果.m文件中寫了@synthesize myButton;
那么生成的實例變量就是myButton
凰棉;如果沒寫@synthesize myButton;
那么生成的實例變量就是_myButton
。所以跟以前的用法還是有點細(xì)微的區(qū)別陌粹。
七:Category與Extension
匿名類別(匿名擴(kuò)展)是可以添加實例變量的
1撒犀、Extension的基本用法
Extension的創(chuàng)建方法與Category一樣,只要在原來選擇Category選擇Extension即可掏秩,比如我們?yōu)镻erson創(chuàng)建一個名為MyExtension的Extension或舞,則最終會生成一個Person_MyExtension.h文件:
// Person_MyExtension.h
#import "Person.h"
@interface Person ()
@end
但要注意的是和Category不同的是它不會生成Person_MyExtension.m文件。之后我們可以在Person_MyExtension.h中直接添加成員變量蒙幻、屬性和方法映凳,如下:
// Person_MyExtension.h
#import "Person.h"
@interface Person ()
{
NSString * _address;
}
@property (nonatomic) NSInteger age;
-(NSString*)WhereAmI;
@end
他常用的形式不是創(chuàng)建一個單獨的文件,而是在實現(xiàn)文件中添加私有的成員變量邮破、屬性和方法诈豌。例如:
// Person.m
#import "Person.h"
@interface Person ()
{
NSString * _address;
}
@property (nonatomic) NSInteger age;
-(NSString*)WhereAmI;
@end
@implementation Person
-(NSString*)WhereAmI{
return @"哪里";
}
@end
2仆救、Extension與Category區(qū)別
- Extension
- 在編譯器決議,是類的一部分队询,在編譯器和頭文件的@interface和實現(xiàn)文件里的@implement一起形成了一個完整的類派桩。
- 伴隨著類的產(chǎn)生而產(chǎn)生,也隨著類的消失而消失蚌斩。
- Extension一般用來隱藏類的私有消息,你必須有一個類的源碼才能添加一個類的Extension范嘱,所以對于系統(tǒng)一些類送膳,如NSString,就無法添加類擴(kuò)展
- Category
- 是運行期決議的
- 類擴(kuò)展可以添加實例變量丑蛤,分類不能添加實例變量
原因:因為在運行期叠聋,對象的內(nèi)存布局已經(jīng)確定,如果添加實例變量會破壞類的內(nèi)部布局受裹,這對編譯性語言是災(zāi)難性的碌补。