一、協議和代理模式 1.在NSObject.h頭文件中,我們可以看到// NSObject類是預設遵守協議的@interface NSObject { Class isa OBJC_ISA_AVAILABILITY;}// 往上翻看到NSObject協議的聲明@protocol NSOb...
一、<NSObject>協議和代理模式
1.在NSObject.h頭文件中,我們可以看到
// NSObject類是預設遵守<NSObject>協議的 @interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; }
// 往上翻看到NSObject協議的聲明
@protocol NSObject
/*
中間一大堆方法的聲明
*/
@end
然後我就產生疑問了,為什麼我們自己定義的協議是這樣,後面加上了<NSObject>。為什麼我們自己聲明的協議需要遵守<NSObject>協議?
我們都知道,遵守一個協議之後就擁有的該協議中的所有方法的聲明。
@protocol MyProtocol <NSObject>
/*
中間一大堆方法的聲明
*/
@end
2.代理模式
1> 代理模式:其實就是把自己的事情交給自己的代理去辦。A去做一件事,但是他做不到或者他不想做,那麼A就去請一個代理B,並且A還要定義一份協議(協議中聲明要做的事),只要B遵守了這份協議(表示B有能力幫A完成這些事),就按照這份協議把事情交給B去做。
有一句話:誰想做什麼事,誰就定義協議,並設置一個代理;誰想幫做什麼事,誰就遵守協議並實現方法。
2> 下麵以一個例子來說明:
有一個boss類
//
// Boss.h
// 代理模式
//
// Created by Silence on 16/1/26.
// Copyright © 2016年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
// boss有一份協議<ZhuLiDelegate>
@protocol ZhuLiDelegate <NSObject>
// 助理掃地的方法
-(void)zhuLiSaoDi;
@end
@interface Boss : NSObject
// 老闆有一個助理Deegate,並且這個助理是遵守了ZhuLiDelegate協議的
// 註意:這裡我用的strong修飾的這個delegate(即Boss擁有一個助理的屬性Delegate,並且是強引用),但是我並沒有在Proxy類中聲明一個Boss的屬性(即助理有一個老闆的屬性),老闆與助理之間並沒有造成迴圈引用的問題。
@property (nonatomic,strong)id<ZhuLiDelegate> delegate;
// 老闆想要掃地
-(void)saoDi;
@end
//
// Boss.m
// 代理模式
//
// Created by Silence on 16/1/26.
// Copyright © 2016年 Silence. All rights reserved.
//
#import "Boss.h"
@implementation Boss
// 老闆想要掃地(實現)
-(void)saoDi
{
// 老闆想要掃地,但是不想自己掃,所以他先查看自己的助理會不會掃地
if ([self.delegate respondsToSelector:@selector(zhuLiSaoDi)])
{
// 如果助理會掃地,那麼老闆就叫助理去掃地(調用助理的zhuLiSaoDi方法)
[self.delegate zhuLiSaoDi];
}
}
// 說明:respondsToSelector方法,判斷某一個對象是否能夠響應該方法
@end
有一個助理類Proxy
//
// Proxy.h
// 代理模式
//
// Created by Silence on 16/1/26.
// Copyright © 2016年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Boss.h"
// 助理類遵守Boss類定義的協議ZhuLiDelegate
@interface Proxy : NSObject<ZhuLiDelegate>
// 那麼就表示Proxy有了該協議下的方法聲明
@end
//
// Proxy.m
// 代理模式
//
// Created by Silence on 16/1/26.
// Copyright © 2016年 Silence. All rights reserved.
//
#import "Proxy.h"
@implementation Proxy
// 實現協議中的方法
-(void)zhuLiSaoDi
{
NSLog(@"助理去掃地!!!");
}
@end
主函數中
#import <Foundation/Foundation.h>
#import "Proxy.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Proxy *proxy = [[Proxy alloc] init];
Boss *boss = [[Boss alloc] init];
// 老闆找一個助理對象作為自己的代理
boss.delegate = proxy;
// 老闆想掃地(調用的自己掃地方法,實際是在裡面調用的助理掃地的方法)
[boss saoDi];
}
return 0;
}
// 列印
2016-01-26 14:47:33.817 代理模式[1348:73812] 助理去掃地!!!
以上就是整個代理模式的實現了。
3.繼續回到 1.中的那個問題,為什麼自己定義的協議要遵守<NSObject>協議?
然後我嘗試把上面的<zhuLiDelegate>協議聲明處的<NSObject>刪掉,編譯之後出現錯誤,如下;
註意:respondsToSelector方法是在<NSObject>協議中聲明的方法。
這裡我們發現self.delegate對象不識別這個方法了,回到delegate聲明處:
@property (nonatomic,strong)id<ZhuLiDelegate> delegate;
// 這個delegate遵守了<ZhuLiDlegate>協議,但是我們把<ZhuLiDlegate>協議遵守<NSObject>協議的地方刪除了,也就表示self.delegate失去了<NSObject>協議中的所有方法聲明,所以就導致方法不可識別了
// 另外,還需明白協議是可以繼承了,既然所有NSObject類預設遵守了<NSObject>協議,那麼就表示所有繼承自NSObject的對象都擁有<NSObject>協議中的方法。除非你像上面一樣遵守自己的協議,並且自己的協議並不遵守基協議<NSObject>,這樣你的對象就無法調用了<NSObject>中的方法了。
二、<NSObject>協議的方法
1.我們都知道,協議中只能聲明一大堆方法,但是我們可以看到<NSObject>中有這麼幾個“屬性”
@property (readonly) NSUInteger hash;
@property (readonly) Class superclass;
@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;
實際上這些和在分類中增加屬性一樣,這裡只會為你生成相應的set、get方法,並不會生成相應的成員變數(實例變數)
拿上面的代理模式做測試,我在<zhuLiDelegate>協議,我在其中加了一個name的“屬性”(始終記住:生成set、get方法)
然後在遵守該協議的Proxy類中實現了該set、get方法(具體參考我的上一篇文章”【ios學習基礎】OC類的相關”中的在分類實現添加屬性),在這裡還是貼上代碼
// 假如沒有實現相應set、get方法,用.屬性訪問會崩潰。
#import "Proxy.h"
#import <objc/runtime.h>
static void *strKey = &strKey;
@implementation Proxy
-(void)setName:(NSString *)name
{
objc_setAssociatedObject(self, &strKey, name, OBJC_ASSOCIATION_COPY);
}
-(NSString *)name
{
return objc_getAssociatedObject(self, &strKey);
}
// 實現協議中的方法
-(void)zhuLiSaoDi
{
NSLog(@"助理去掃地!!!");
}
@end
主函數中
#import <Foundation/Foundation.h>
#import "Proxy.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
Proxy *proxy = [[Proxy alloc] init];
Boss *boss = [[Boss alloc] init];
// 老闆找一個助理對象作為自己的代理
boss.delegate = proxy;
proxy.name = @"協議:name屬性";
NSLog(@"%@",proxy.name);
// 老闆想掃地(調用的自己掃地方法,實際是在裡面調用的助理掃地的方法)
[boss saoDi];
}
return 0;
}
// 列印
2016-01-26 15:33:03.625 代理模式[1456:93614] 協議:name屬性
2016-01-26 15:33:03.627 代理模式[1456:93614] 助理去掃地!!!
2.方法介紹
1> - (BOOL)isEqual:(id)object; 比較兩個對象的地址是否相等
2> - (id)performSelector:(SEL)aSelector; 調用sSelectopr方法
- (id)performSelector:(SEL)aSelector withObject:(id)object; 調用sSelectopr方法,傳一個參數
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; 調用sSelectopr方法,傳兩個參數
這裡簡單介紹一下SEL類型:
我們都知道,每一個繼承自NSObject對象都有一個isa指針,指向“類的方法列表”(類也有存儲空間,既也有地址,一個類在存儲空間中僅此一份)
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
SEL就是對方法的一種包裝。包裝的SEL類型數據它對應相應的方法地址,找到方法地址就可以調用方法。在記憶體中每個類的方法都存儲在類對象中,每個方法都有一個與之對應的SEL類型的數據,根據一個SEL數據就可以找到對應的方法地址,進而調用方法。
SEL類型的定義: typedef struct objc_selector *SEL
SEL的使用:SEL S1 = @selector(test); 將test方法包裝成SEL對象(無參)
SEL S2= @selector(test:); 將test方法包裝成SEL對象(有參)
SEL S3 = NSSelectorFromString(@"test"); 將一個字元串方法轉換成為SEL對象
3> - (BOOL)isProxy; 判斷一個實例是否繼承自NSObject,如果返回NO就是繼承自NSObject,反之返回YES
4> - (BOOL)isKindOfClass:(Class)aClass; 判斷對象是否屬於aClass及其子類
- (BOOL)isMemberOfClass:(Class)aClass; 判斷對象是否屬於aClass類
- (BOOL)conformsToProtocol:(Protocol *)aProtocol; 檢查某個對象是否遵守了aProtocol協議
- (BOOL)respondsToSelector:(SEL)aSelector; 判斷對象是否能響應aSelector方法
5> 記憶體管理相關
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
三、NSObject的方法
1.初始化相關
+ (void)load; 類的頭文件被引入就會調用
+ (void)initialize; 類或其子類的第一個方法被調用之前調用
- (instancetype)init 初始化
+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); 創建一個對象
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); alloc方法內部調用該方法,返回分配的存儲空間zone
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); 分配存儲空間
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer"); 對象銷毀時調用
2.方法相關
+ (BOOL)instancesRespondToSelector:(SEL)aSelector; 判斷類是否有aSelector方法
+ (BOOL)conformsToProtocol:(Protocol *)protocol; 判斷類是否遵守此協議
- (IMP)methodForSelector:(SEL)aSelector; 根據一個SEL,得到該方法的IMP(函數指針)
+ (IMP)instanceMethodForSelector:(SEL)aSelector; 類方法,返回的是類方法的真正的函數地址
- (void)doesNotRecognizeSelector:(SEL)aSelector; 處理接收者無法識別的消息
- (id)forwardingTargetForSelector:(SEL)aSelector; 當某個對象不能接受某個selector時,將對該selector的調用轉發給另一個對象
3.+ (BOOL)isSubclassOfClass:(Class)aClass; 判斷一個類是否是其子類
+ (NSUInteger)hash; 如果isEqual判斷兩個對象相等,那麼兩個對象的hash返回值也一定相等。反之hsah值相等,isEqual未必認為兩者一樣。
+ (Class)superclass; 獲得父類類對象
+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead"); 獲得本類類對象
+ (NSString *)description; NSLog列印格式。
+ (NSString *)debugDescription; 打斷點時看到的格式。