1.屬性
使用屬性,編譯器在編譯期,自動合成,setter方法,getter方法與帶下劃線的實例變量
屬性 = setter + getter + 實例變量
如果自己實現(xiàn)了其中一種方法,那么另外一種方法編譯器來合成
如果不想編譯器自動合成,可以使用關鍵字@dynamic. 這個時候不會自動生成setter與getter的實現(xiàn)(會生成聲明)與屬性.而且編譯期間也不會報錯
assign:使用assign來修飾對象類型的時候,當目標對象銷毀的時候,屬性值不會自動設置為nil
weak:使用weak來修飾對象類型的時候,當目標對象銷毀的時候,屬性值會自動設置為nil
unsafe_unretained:特質與assign相同,但是它只適用于"對象類型",對象銷毀不會自動設置為nil
2.在對象內部盡量直接訪問實例變量
建議:除特殊情況之外
讀取實例變量的時候采用直接訪問的形式(例如懶加載除外)
設置實例變量的時候通過屬性來做
由于不經(jīng)過OC的方法派發(fā),所以直接訪問實例變量的速度比較快,這種情況下,編譯器所生成的代碼會直接訪問保存對象實例變量的那塊內存
3.理解"對象等同性"這一概念
NSString *one = @"123";
NSString *two = [NSString stringWithFormat:@"123"];
NSLog(@"%p,%p",one,two);//0x10f844078,0xa000000003332313
NSLog(@"%d",(one == two));//0
NSLog(@"%d",[one isEqual:two]);//1
NSLog(@"%d",[one isEqualToString:two]);//1
NSLog(@"%lu",(unsigned long)[one hash]);//487557729
NSLog(@"%lu",(unsigned long)[two hash]);//487557729
4.以"類簇模式"隱藏實現(xiàn)細節(jié)
可以隱藏"抽象基類"背后的實現(xiàn)細節(jié), 比如UIKit中的UIButton類,可以通過
+(UIButton*)buttonWithType:(UIButtonType)type;
該方法返回的對象,類型取決與傳入的buttonType.然后不管是什么類型,他們都繼承基類UIButton,
好處在于:UIButton類的使用者無需關系創(chuàng)建出來的按鈕具體屬于哪個子類
4.1創(chuàng)建類簇舉例
typedef NS_ENUM(NSUInteger,EOCEmployeeType) {
EOCEmployeeTypeDeveloper,
EOCEmployeeTypeDesigner,
EOCEmployeeTypeFinance
};
@interface EOCEmployee : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger salary;
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type;
- (void)doADayWorks;
@end
#import "EOCEmployee.h"
#import "EOCEmployeeDeveloper.h"
#import "EOCEmployeeDesign.h"
@implementation EOCEmployee
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type{
? ? switch (type) {
? ? ? ? case EOCEmployeeTypeDeveloper:
? ? ? ? ? ? return [[EOCEmployeeDeveloper alloc] init];
? ? ? ? ? ?break;
? ? ? case EOCEmployeeTypeDesigner:
? ? ? ? ? return [[EOCEmployeeDesign alloc] init];
? ? ? ? ? break;
? ? ?case EOCEmployeeTypeFinance:
? ? ? ? return nil;
? ? ? ?break; ?
? ?}
}
- (void)doADayWorks{
//subclasses implement this
}
4.2子類實現(xiàn)
#import "EOCEmployeeDeveloper.h"
@implementation EOCEmployeeDeveloper
- (void)doADayWorks{
NSLog(@"write code");
}
@end
4.3使用
EOCEmployee *employeeDev = [EOCEmployee employeeWithType:EOCEmployeeTypeDeveloper];
[employeeDev doADayWorks];
EOCEmployee *employeeDes = [EOCEmployee employeeWithType:EOCEmployeeTypeDesigner];
[employeeDes doADayWorks];
5.在既有類中使用關聯(lián)對象存放自定義數(shù)據(jù)
我們通常會使用子類來實現(xiàn),但是有的類并不能通過繼承來解決, 可以通過Associated Object關聯(lián)對象來實現(xiàn)
void objc_setAssociatedObject(idobject,constvoid*key,idvalue, objc_AssociationPolicy policy)
id objc_getAssociatedObject(idobject,constvoid*key)
void objc_removeAssociatedObjects(idobject)
eg
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"警告" message:@"地球危險" delegate:self cancelButtonTitle:@"知道了" otherButtonTitles:@"回去火星", nil];
Block block = ^(NSInteger index){
? ? if (index == 1){
? ? ? NSLog(@"地球太危險,跟我回火星");
? ? }else{
? ? ? NSLog(@"我是奧特曼我怕誰");
? ? }
};
objc_setAssociatedObject(alertView, "kAlertViewBlockKey", block, OBJC_ASSOCIATION_COPY_NONATOMIC);
[alertView show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
? ? Block block = objc_getAssociatedObject(alertView, "kAlertViewBlockKey");
? ? block(buttonIndex);
}
6.理解objc_msgSend
void objc_msgSend(id self,SEL cmd,...)
第一個參數(shù):消息接受者
第二個參數(shù):選擇器
…:后面的為參數(shù)(如果有)
發(fā)送消息:首先接受者去所屬類的list of methods中查找與名稱相符的方法,然后跳轉到實現(xiàn)代碼如果沒有找到,沿著繼承體系去找,找到再跳轉,最終都沒找到,則執(zhí)行message forwarding
注意:objc_msgSend會將匹配結果緩存到fast map.每個類都有一個緩存,稍后還想該類發(fā)送與選擇器相同的方法那么執(zhí)行起來就會更快
7.理解消息轉發(fā)機制
在編譯器向類發(fā)送了其無法解讀的消息并不會報錯,因為在運行可以繼續(xù)向類中添加方法
運行期當對象接受到無法解讀的消息就會啟動message forwarding
//unrecognized selector sent to instance 0x87...
第一階段:動態(tài)方法解析 dynamic method resolution
第二階段:完整的消息轉發(fā)機制 ?full forwading mechanism
1>.動態(tài)方法解析
對象在收到無法解讀的消息后,首先調用下列類方法
+ (BOOL)resolveInstanceMethod:(SEL)selector
//selector為未知的選擇器,
//返回值BOOL類型,表示這個類是否能新增一個實例方法用于處理選擇器
//如果未實現(xiàn)的方法不是實例方法而是類方法, 則會調用"resolveClassMethod"
注意:使用這種辦法的前提,相關方法的實現(xiàn)代碼已經(jīng)寫好,只等著運行的時候動態(tài)插在類里面就可以
此方案常用來實現(xiàn)@dynamic.
2>備用接受者
當前接受者有第二次機會能處理未知的選擇器,這一步,運行系統(tǒng)會問:能否把這條消息轉給其他接受者處理
- (id)forwardingTargetForSelector:(SEL)sector
通過該方法可以通過組合來模擬"多重繼承".在一個對象內部,有一系列其他對象,該對象可以在此方法將能處理某選擇器的相關對象返回
3>完整的消息轉發(fā)
如果forwardingTargetForSelector返回nil,則會到完整的消息轉發(fā),首先創(chuàng)建一個NSInvocation對象,此對象包含選擇器(selector),目標(target),參數(shù).然后執(zhí)行
- (void)forwardInvocation:(NSInvocation *)invocation
①.這個方法可以實現(xiàn)的很簡單,只需改變調用目標,是消息在新目標上執(zhí)行,這樣做與"備用接受者"等效,無意義②.在觸發(fā)消息之前,先以某種方式,改變消息內容,比如更好選擇器,追加參數(shù)等
8.用"方法調配技術" 調試"黑盒方法"
在運行期間,可以向類中新增或者替換選擇器所對應的方法實現(xiàn)
使用一份實現(xiàn)來替換原有的方法實現(xiàn),叫做"方法調配",常用此技術向原有實現(xiàn)中添加新功能
MethodoriginalMethod =class_getInstanceMethod([NSStringclass],@selector(uppercaseString));
MethodswappedMethod =class_getInstanceMethod([NSStringclass],@selector(lowercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
9.理解"類對象"的用意
編譯器無法確定某類型對象到底能解讀多少種選擇器,因為運行期還可以向其中動態(tài)新增
"在運行期檢視對象類型"叫做"類型信息查詢"(內省).這個特性內置于Foundation框架
的NSObject協(xié)議中,凡是由公共根類繼承來的對象都要遵守這個協(xié)議
id 類型是objc_object *類型的指針
typedefstructobjc_object *id;
objc_object對象結構體有個Class類的的變量isa. 該變量定義了對象所屬的類.
structobjc_object {
Class isa? OBJC_ISA_AVAILABILITY;
};
Class類型是objc_class類型的結構體指針
typedefstructobjc_class *Class;
objc_class結構體存放"元數(shù)據(jù)",例如類的實例實現(xiàn)了幾個方法,具備多少個實例變量等消息
該結構體的第一個變量也是isa指針,說明Class本身也是Objective-C對象.
super_class定義了本類的超類,類對象所屬的類型(isa指向的類)是另外一個類,叫元類,用來描述類對象的元數(shù)據(jù)
"類方法"就定義與此處,這些方法可以理解為類對象的實例方法.每個類只有一個類對象
structobjc_class {
Class isa? OBJC_ISA_AVAILABILITY;
Class super_class??????????????????????????????????????? OBJC2_UNAVAILABLE;
constchar*name ??????????????????????????????????????? OBJC2_UNAVAILABLE;
longversion ??????????????????????????????????????????? OBJC2_UNAVAILABLE;
longinfo??????????????????????????????????????????????? OBJC2_UNAVAILABLE;
longinstance_size ????????????????????????????????????? OBJC2_UNAVAILABLE;
structobjc_ivar_list *ivars ??????????????????????????? OBJC2_UNAVAILABLE;
structobjc_method_list **methodLists??????????????????? OBJC2_UNAVAILABLE;
structobjc_cache *cache ??????????????????????????????? OBJC2_UNAVAILABLE;
structobjc_protocol_list *protocols ??????????????????? OBJC2_UNAVAILABLE;
}
1>在類繼承體現(xiàn)中查詢類型消息
- (BOOL)isKindOfClass:(Class)aClass//判斷對象是否為某個類或者派生類的實例
- (BOOL)isMemberOfClass:(Class)aClass//判斷對象是否為某個特定類的實例
盡量使用類型信息查詢方法來確定對象類型,而不是直接比較類對象("=="). 因為某些對象可能實現(xiàn)了消息轉發(fā)功能