Working with Protocols
定義
類的接口用來聲明其自身相關(guān)的methods和properties咖为;相反易猫,協(xié)議用于聲明獨立于任何特定類的方法和屬性著隆。
基本定義語法:聲明實例方法狡相、類方法、屬性
@protocol ProtocolName
// list of methods and properties
@end
協(xié)議可以繼承其它協(xié)議褪迟;NSObject提供了很多常用的協(xié)議冗恨,因此常見的定義方式
@protocol MyProtocol <NSObject>
// list of methods and properties
@end
用法
下面舉例說明用法答憔。例如想要繪制如下圖的餅圖
為了使得pie chart view的代碼能夠重復(fù)使用,將決定展示內(nèi)容的代碼放到另一個對象中--Datasource掀抹。這樣可以達(dá)到只要更換數(shù)據(jù)源對象就可以展示不同的內(nèi)容攀唯。
比如說餅圖分幾部分、各部分占得比例渴丸、各部分的標(biāo)題的協(xié)議如下
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
Datasource
協(xié)議定義好了侯嘀,此view還需要一個property來記錄datasource對象,這個對象可能是任何類的實例谱轨,因此聲明為id類型戒幔;對于view來說只知道此對象遵守XYZPieChartViewDataSource協(xié)議。Objective-C使用尖括號來表示遵守此協(xié)議土童。
@interface XYZPieChartView : UIView
@property (weak) id <XYZPieChartViewDataSource> dataSource;
...
@end
注意:
1诗茎、此處的weak來修飾datasource是為了避免循環(huán)引用。
2献汗、datasource對象需要聲明遵守協(xié)議敢订,否則編譯器發(fā)布警告
3、對于view來說datasource的類型不重要罢吃,重要是遵守并實現(xiàn)了協(xié)議
必選楚午、可選協(xié)議
1、通過編譯器指令@optional指定跟在后邊的方法均為可選尿招;
2矾柜、通過編譯器指令@required指定跟在后邊的方法均為必選;
3就谜、不聲明則默認(rèn)必選怪蔑;
例如
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
- (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
表示聲明了3個必選方法和2個可選方法。
對于可選方法丧荐,要在runtime期間通過respondsToSelector:
方法檢測對象是否已實現(xiàn)缆瓣;例如
//局部對象變量自動初始化為nil
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
協(xié)議繼承
像類繼承一樣,協(xié)議也可以繼承虹统。例如最好使得自定義協(xié)議繼承NSObject協(xié)議(NSObject接口分離出來組成的獨立的協(xié)議)弓坞。由于MyProtocol繼承NSObject protocol,所有凡是遵守MyProtocol的對象都將實現(xiàn)NSObject protocol中聲明的方法窟却,然后你并不擔(dān)心這些方法的實現(xiàn)昼丑,因為使用的絕大多數(shù)類都繼承NSObject,已經(jīng)默認(rèn)實現(xiàn)過了夸赫。協(xié)議定義變成這樣
@protocol MyProtocol <NSObject>
// list of methods and properties
@end
In this example, any object that adopts MyProtocol also effectively adopts all the methods declared in the NSObject protocol.
遵守協(xié)議
通過尖括號來聲明一個類遵守協(xié)議,例如
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
...
@end
提示:
1咖城、如果一個類遵守的協(xié)議過多茬腿,不便于維護(hù)呼奢,建議分割成獨立的類去實現(xiàn)。
2切平、如果未實現(xiàn)協(xié)議要求必須實現(xiàn)的方法握础,編譯器將發(fā)出警告。
3悴品、實現(xiàn)協(xié)議方法時禀综,方法名稱、參數(shù)類型必須和協(xié)議聲明的保持一致苔严。
協(xié)議屏蔽類的實現(xiàn)
協(xié)議除了讓不同的類按照指定的方式完成一定的任務(wù)之外定枷,還將功能抽離出來提高代碼復(fù)用率。協(xié)議的標(biāo)識的是相互協(xié)作的類之間沒有層次關(guān)系届氢,這樣使得不相干的類可以使用相同的功能欠窒。比如說NSArray和NSDictionary都遵守NSCoding協(xié)議,使得其本身能方便的進(jìn)行本地存儲退子;遵守NSFastEnumeration協(xié)議岖妄,使得能夠快速枚舉元素。
協(xié)議除了上述作用之外寂祥,還能屏蔽類的實現(xiàn)荐虐。當(dāng)不知道某個對象的所屬類時即id或者需要隱藏的時候,協(xié)議可以起到很好的作用丸凭。例如缚俏,開發(fā)者框架中可能沒有發(fā)布類的接口聲明,由于class未知贮乳,不能夠直接創(chuàng)建實例對象
id utility = [frameworkObject anonymousUtility];
為了anonymousUtility對象能夠使用忧换,SDK發(fā)不了一個協(xié)議來暴露其部分方法,盡管對class一無所知向拆,但是還能勉強能夠使用了
id <XYZFrameworkUtility> utility = [frameworkObject anonymousUtility];
此時utility變量可以使用XYZFrameworkUtility中聲明的方法亚茬。
參考文獻(xiàn):Working with Protocols
Runtime--Protocols
查詢協(xié)議相關(guān)信息
首先先來聲明一個協(xié)議DataDelegate,ViewController類遵守浓恳,然后結(jié)合例子說明接口
@protocol DataDelegate <NSObject>
@property(nonatomic,copy)NSString *name;
-(void)ivarMethod;
@end
1刹缝、獲取所有runtime時的協(xié)議
/* 獲取所有協(xié)議名稱 */
unsigned int protocol_count = 0;
__unsafe_unretained Protocol **protocol_list = objc_copyProtocolList(&protocol_count);
for (int i = 0; i < protocol_count ; i ++)
{
Protocol *protocol = protocol_list[i];
const char *name_class = protocol_getName(protocol);//名稱
NSLog(@"index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(protocol_list);
//輸出結(jié)果:過多僅做部分展示
index:0 name_class:SBSRemoteAlertClientHandle
index:1 name_class:_UIContentContainerInternal
index:2 name_class:NSFetchRequestResult
index:3 name_class:CAMLWriterDelegate
index:4 name_class:CABehaviorDelegate
index:5 name_class:GEOTransitSystem
index:6 name_class:GEOMapItemPhoto
index:7 name_class:UIInputViewAnimationHost
index:8 name_class:UIAdaptivePresentationControllerDelegate
index:9 name_class:_UISharingPublicController
index:10 name_class:NSISEngineDelegate
index:11 name_class:UIPopoverPresentationControllerDelegate
2、根據(jù)名稱獲取協(xié)議
/* 獲取協(xié)議 */
Protocol *protocol = objc_getProtocol("DataDelegate");
3颈将、獲取名稱
/* 獲取名稱 */
const char *pro_name_char = protocol_getName(protocol);
NSString *protocolName = [NSString stringWithUTF8String:pro_name_char];
NSLog(@"protocolName:%@",protocolName);
//輸出結(jié)果
protocolName:DataDelegate
4梢夯、獲取方法描述
/* 獲取方法描述 */
struct objc_method_description protocol_method_description = protocol_getMethodDescription(protocol, @selector(ivarMethod), YES, YES);
NSLog(@"name:%@ type:%@",NSStringFromSelector(protocol_method_description.name),[NSString stringWithUTF8String:protocol_method_description.types]);
//輸出結(jié)果:空返回、無參數(shù)
name:ivarMethod type:v16@0:8
/* 獲取方法列表描述 */
unsigned int methodCount = 0;
struct objc_method_description *method_description_list = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount);
for (int i = 0; i < methodCount ; i ++)
{
struct objc_method_description description = method_description_list[i];
NSLog(@"index:%d name:%@ type:%@",i,NSStringFromSelector(description.name),[NSString stringWithUTF8String:description.types]);
}
free(method_description_list);
//輸出結(jié)果
index:0 name:ivarMethod type:v16@0:8
index:1 name:name type:@16@0:8
index:2 name:setName: type:v24@0:8@16
5晴圾、獲取property
/* 獲取property */
objc_property_t property = protocol_getProperty(protocol, "name", YES, YES);
const char *propeytyName = property_getName(property);
NSLog(@"propeytyName:%@",[NSString stringWithUTF8String:propeytyName]);
//輸出結(jié)果
propeytyName:name
/* 獲取property列表 */
unsigned int propertyCount = 0;
objc_property_t *property_list = protocol_copyPropertyList(protocol, &propertyCount);
for (int i = 0; i < propertyCount ; i ++)
{
objc_property_t property = property_list[i];
const char *propeytyName = property_getName(property);
NSLog(@"index:%d propeytyName:%@",i,[NSString stringWithUTF8String:propeytyName]);
}
free(property_list);
//輸出結(jié)果
index:0 propeytyName:name
6颂砸、獲取繼承的協(xié)議列表
/* 獲取繼承的協(xié)議 */
unsigned int protocol_count = 0;
__unsafe_unretained Protocol **protocol_list = protocol_copyProtocolList(protocol, &protocol_count);
for (int i = 0; i < protocol_count ; i ++)
{
Protocol *protocol = protocol_list[i];
const char *name_class = protocol_getName(protocol);//名稱
NSLog(@"index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(protocol_list);
//輸出結(jié)果
index:0 name_class:NSObject
7、判斷兩個協(xié)議是否相等
/* 是否相等*/
BOOL isEqual = protocol_isEqual(protocol, @protocol(DataDelegate));
NSLog(@"是否相等:%d",isEqual);
//輸出結(jié)果
是否相等:1
8、判斷協(xié)議A是否遵守(繼承)協(xié)議B
/* 是否遵守 */
BOOL isconform = protocol_conformsToProtocol(protocol, @protocol(NSObject));
NSLog(@"是否遵守:%d",isconform);
//輸出結(jié)果
是否遵守:1
創(chuàng)建新的協(xié)議
OBJC_EXPORT Protocol *objc_allocateProtocol(const char *name)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
OBJC_EXPORT void objc_registerProtocol(Protocol *proto)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
說明
1人乓、新的協(xié)議只有被register才能使用
2勤篮、新的協(xié)議可以添加Method、Property色罚、遵守的Protocol
3碰缔、所有的添加操作都必須在objc_allocateProtocol和objc_registerProtocol直接進(jìn)行,也就是說在協(xié)議的構(gòu)建期間進(jìn)行
在上述代碼的基礎(chǔ)上
/* 創(chuàng)建新的協(xié)議 */
Protocol *newProtocol = objc_allocateProtocol("DataDelegate2");
/* 添加property */
objc_property_attribute_t attr_T = {"T","@\"NSString\""};//@encod
objc_property_attribute_t attr_N = {"N",""};//原子性
objc_property_attribute_t attr_C = {"C",""};//copy
objc_property_attribute_t attr_V = {"V","_age"};//實例變量
objc_property_attribute_t attrs[] = {attr_T,attr_N,attr_C,attr_V};
protocol_addProperty(newProtocol, "age", attrs, 4, YES, YES);
/* 添加遵守的協(xié)議 */
protocol_addProtocol(newProtocol, @protocol(NSObject));
/* 添加方法方法 */
protocol_addMethodDescription(newProtocol, @selector(ivarMethod2), "v@:", YES, YES);
/* 注冊新協(xié)議 */
objc_registerProtocol(newProtocol);
//獲取property列表
unsigned int propertyCount2 = 0;
objc_property_t *property_list2 = protocol_copyPropertyList(newProtocol, &propertyCount2);
for (int i = 0; i < propertyCount2 ; i ++)
{
objc_property_t property = property_list2[i];
const char *propeytyName = property_getName(property);
NSLog(@"2 index:%d propeytyName:%@",i,[NSString stringWithUTF8String:propeytyName]);
}
free(property_list2);
//獲取方法列表
unsigned int methodCount2 = 0;
struct objc_method_description *method_description_list2 = protocol_copyMethodDescriptionList(newProtocol, YES, YES, &methodCount2);
for (int i = 0; i < methodCount2 ; i ++)
{
struct objc_method_description description = method_description_list2[i];
NSLog(@"2 index:%d name:%@ type:%@",i,NSStringFromSelector(description.name),[NSString stringWithUTF8String:description.types]);
}
free(method_description_list2);
//獲取新協(xié)議遵守的協(xié)議
unsigned int protocol_count2 = 0;
__unsafe_unretained Protocol **protocol_list2 = protocol_copyProtocolList(newProtocol, &protocol_count2);
for (int i = 0; i < protocol_count2 ; i ++)
{
Protocol *protocol = protocol_list2[i];
const char *name_class = protocol_getName(protocol);//名稱
NSLog(@"2 index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(protocol_list2);
//輸出結(jié)果
2 index:0 propeytyName:age//property
2 index:0 name:ivarMethod2 type:v@://Method
2 index:0 name_class:NSObject//protocol
協(xié)議添加完成后是需要指定類使用的
/* 檢測類是否遵守指定的協(xié)議 */
BOOL conform = class_conformsToProtocol([ViewController class], protocol);
NSLog(@"conform:%d",conform);
/* 添加新的協(xié)議 */
class_addProtocol([ViewController class], newProtocol);
/* 獲取類遵守的協(xié)議列表 */
unsigned int classProtocolCount = 0;
__unsafe_unretained Protocol **classProtocolList = class_copyProtocolList([ViewController class], &classProtocolCount);
for (int i = 0; i < classProtocolCount ; i ++)
{
Protocol *protocol = classProtocolList[i];
const char *name_class = protocol_getName(protocol);//名稱
NSLog(@"class index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(classProtocolList);
//輸出結(jié)果
conform:1 //遵守
class index:0 name_class:DataDelegate2 //添加完成
class index:1 name_class:DataDelegate //本來就遵
參考文獻(xiàn):Objective-C Runtime戳护、Type Encodings