objective-c中的正式协议 protocol(十一)

来源:互联网 发布:mac开机选择用户 编辑:程序博客网 时间:2024/05/01 10:48

上一往篇文章中我们谈到了非正式协议类别,可以在一个类中补充方法。但是这个方法你可以不去调用。协议类似于java的接口,如果一个协议中定义了某些方法,而某类又实现了该协议,那么该类必须实现这些方法。换句话说,协议是一组公用的方法声明,谁实现协议,谁就负责实现这些方法,不然会有黄色警告。协议可以扩展已有协议。协议的关键字是protocol,以@protocol开始声明,以@end结束。在类中实现协议时,只需要在类名后面加个<协议名>,即可。下面 看代码:

先定义一个协议:eat.h

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @protocol eat <NSObject>//协议eat扩展了协议NSObject.h  
  4. -(void)eat;  
  5. @end  

上面扩展的NSObject协议不用实现,因为继承于NSObject的类已经继承了对NSObject协议的实现

下面创建一个类:Human.h

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "eat.h"  
  3. @interface Human : NSObject <eat>  
  4.   
  5. @end  

Human.m

[plain] view plaincopy
  1. #import "Human.h"  
  2.   
  3. @implementation Human  
  4. -(void)eat  
  5. {  
  6.     NSLog(@"协议中定义的eat");  
  7. }  
  8. @end  

在main.m中调用:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "eat.h"  
  3. #import "Human.h"  
  4. int main(int argc, const char * argv[])  
  5. {  
  6.   
  7.     @autoreleasepool {  
  8.         Human *human =[[Human alloc] init];  
  9.         [human eat];  
  10.           
  11.     }  
  12.     return 0;  
  13. }  

2012-03-18 14:35:29.099 category1[1752:403] 协议中定义的eat


很简单吧,另外,在新版本的objective-c中,增加了协议的一些可选项,@optional,@required,协议中的方法必须实现,不然会报错,但是如果以@optional修饰的话便没有这种限制,默认必须实现的方法其实就相当于以@required修饰,比如上面的代码,我们可以做出以下修改:

eat.h

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @protocol eat <NSObject>//协议eat扩展了协议NSObject.h  
  4. -(void)eat;  
  5. @optional  
  6. -(void)playGuitar;  
  7. @required  
  8. -(void)sleep;  
  9. @end  

这样的话,sleep方法和eat方法同样必须实现,而playGuitar方法便可选实现,是不是很人性化。


objective-c中的本地化操作(序列化,归档)(十二)

先介绍一个自定义类描述的方法description,一般情况下,一个自定义类我们在用%@输出的时候,给出的是一个内存地址,我们在该类的.m文件里重写description方法,来修改输出内容,呆会儿我们要用到这个方法来验证今天学习内容,所以先看一段代码熟悉一下:

Human.h:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface Human : NSObject  
  4. {  
  5.     int age;  
  6.     NSString *name;  
  7.     Human *child;  
  8. }  
  9.   
  10. @property int age;  
  11. @property (copy)NSString *name;  
  12. @property (retain)Human *child;  
  13. @end  

Human.m:

[plain] view plaincopy
  1. #import "Human.h"  
  2.   
  3. @implementation Human  
  4. @synthesize age;  
  5. @synthesize name;  
  6. @synthesize child;  
  7.   
  8. //-(NSString *)description  
  9. //{  
  10. //    NSString *des = [NSString stringWithFormat:@"%d,%@,%@",age,name,child];  
  11. //    return des;  
  12. //}  
  13.   
  14. @end  

上面的重写描述被注释掉了,我们先看未修改前的输出:

main.m:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "Human.h"  
  3.   
  4. int main(int argc, const char * argv[])  
  5. {  
  6.   
  7.     @autoreleasepool {  
  8.         Human *human1=[[Human alloc]init];  
  9.         Human *human2=[[Human alloc]init];  
  10.         human1.child=human2;  
  11.         human1.name=@"holydancer";  
  12.         human1.age=22;  
  13.         NSLog(@"%@",human1);  
  14.               
  15.     }  
  16.     return 0;  
  17. }  

2012-03-20 08:47:32.980 category[304:403] <Human: 0x7ff2cb414380>


如果把human.m中的注释去掉的话输出结果如下:

2012-03-20 08:48:09.869 category[315:403] 22,holydancer,0,(null),(null)


很简单吧,这样就可以查看自己定义类的内容了,好了,下面就让我们来研究一下在objective-c中如何实现序列化。

在OC中,有四类对象是可以直接使用writeToFile方法将内容写入磁盘的,分别是NSString,NSArray,NSDictionary,NSData.看代码:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "Human.h"  
  3.   
  4. int main(int argc, const char * argv[])  
  5. {  
  6.   
  7.     @autoreleasepool {  
  8.       
  9.         NSData *data=[[NSData alloc]init];  
  10.         NSString *string=[[NSString alloc]init];  
  11.         NSArray *array=[[NSArray alloc]init];  
  12.         NSDictionary *dictionary=[[NSDictionary alloc]init];  
  13.                   
  14.         [data writeToFile:@"/Users/holydancer/Desktop/text1.txt" atomically:YES];  
  15.         [string writeToFile:@"/Users/holydancer/Desktop/text2.txt" atomically:YES];  
  16.         [array writeToFile:@"/Users/holydancer/Desktop/text3.txt" atomically:YES];  
  17.         [dictionary writeToFile:@"/Users/holydancer/Desktop/text4.txt" atomically:YES];  
  18.         //atomically参数是指是否将写入文件的内容开启保护机制,如果开启,会在复制时创建临时文件进行复制,以免写入失败破坏原始文件。安全,但是会消耗内存。  
  19.         //上面的文件地址,如果不存在的话会自动生成。有的话会覆盖原有文件内容。      
  20.     }  
  21.     return 0;  
  22. }  



以上四种是COCOA自带可以写入磁盘文件的类型,但是我们常常用到自定义类,可是里面并没有writeToFile方法,怎么办呢?这时NSData的作用就体现出来了,我们可以把任意自定义类转化成NSData格式即可,这个过程我们称之为编码,或者archive归档,需要将自定义类实现NSCoding协议并重写encodeWithCoder和initWithCoder两个方法,分别用以编码和反编码。然后在编码时会用NSCoder的子类NSKeyedArchiver和NSKeyedUnarchiver分别调用archivedDataWithRootObject和unarchiveObjectWithData来启动自定义类中重写的那两个方法,类似于回调。看代码:

Human.h:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface Human : NSObject<NSCoding>  
  4. {  
  5.     int age;  
  6.     NSString *name;  
  7.     Human *child;  
  8. }  
  9.   
  10. @property int age;  
  11. @property (copy)NSString *name;  
  12. @property (retain)Human *child;  
  13. @end  

Human.m:

[plain] view plaincopy
  1. #import "Human.h"  
  2.   
  3. @implementation Human  
  4. @synthesize age;  
  5. @synthesize name;  
  6. @synthesize child;  
  7.   
  8. -(NSString *)description  
  9. {  
  10.     NSString *des = [NSString stringWithFormat:@"%d,%@,%@",age,name,child];  
  11.     return des;  
  12. }  
  13. -(void)encodeWithCoder:(NSCoder *)aCoder//要一一对应  
  14. {  
  15.     [aCoder encodeInt:age forKey:@"age"];  
  16.     [aCoder encodeObject:name forKey:@"name"];  
  17.     [aCoder encodeObject:child forKey:@"child"];  
  18. }  
  19. -(id)initWithCoder:(NSCoder *)aDecoder//和上面对应  
  20. {  
  21.     if (self=[super init]) {  
  22.         self.age=[aDecoder decodeIntForKey:@"age"];  
  23.         self.name=[aDecoder decodeObjectForKey:@"name"];  
  24.         self.child=[aDecoder decodeObjectForKey:@"child"];  
  25.     }  
  26.     return self;  
  27. }  
  28. @end  

main.m:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "Human.h"  
  3. #import <Foundation/NSKeyedArchiver.h>  
  4.   
  5. int main(int argc, const char * argv[])  
  6. {  
  7.   
  8.     @autoreleasepool {  
  9.       
  10.         Human *human1=[[Human alloc]init];  
  11.         Human *human2=[[Human alloc]init];  
  12.         human1.age=20;  
  13.         human1.name=@"holydancer";  
  14.         human1.child=human2;  
  15.         //定义好自定义对象后使用NSCoding的子类调用archivedDataWithRootObject方法进行archive  
  16.         NSData *data1=[NSKeyedArchiver archivedDataWithRootObject:human1];  
  17.         //转成NSData类型后就可以写入本地磁盘了  
  18.         [data1 writeToFile:@"/Users/holydancer/Desktop/tmp.txt" atomically:YES];  
  19.         //倒过来的话先读取磁盘文件  
  20.         NSData *data2=[NSData dataWithContentsOfFile:@"/Users/holydancer/Desktop/tmp.txt"];  
  21.         Human *human3=[NSKeyedUnarchiver unarchiveObjectWithData:data2];  
  22.         NSLog(@"%@,%@",human1,human3);  
  23.     }  
  24.     return 0;  
  25. }  

2012-03-20 10:10:29.871 category[458:403] 

20,holydancer,0,(null),(null)

20,holydancer,0,(null),(null)

有的同学一直不太清楚NSKeyedArchiver和NSKeyedUnarchiver是什么,调用的又是什么方法,大家可以在头文件里找到这样的信息:

可以发现,NSKeyedArchiver是NSCoder的子类,而archivedDataWithRootObject是里面的一个类方法,这时我们看到archivedDataWithRootObject方法下在还有一个方法,不错,这个方法可以直接将自定义类写入本地磁盘,所以上在的代码我们还可以这样写:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "Human.h"  
  3. #import <Foundation/NSKeyedArchiver.h>  
  4.   
  5. int main(int argc, const char * argv[])  
  6. {  
  7.   
  8.     @autoreleasepool {  
  9.       
  10.         Human *human1=[[Human alloc]init];  
  11.         Human *human2=[[Human alloc]init];  
  12.         human1.age=20;  
  13.         human1.name=@"holydancer";  
  14.         human1.child=human2;  
  15.         [NSKeyedArchiver archiveRootObject:human1 toFile:@"/Users/holydancer/Desktop/tmp.txt"];//直接写入磁盘  
  16.         Human *human3=[NSKeyedUnarchiver unarchiveObjectWithFile:@"/Users/holydancer/Desktop/tmp.txt"];//从磁盘直接读取为id类型  
  17.         NSLog(@"\n%@\n%@",human1,human3);  
  18.     }  
  19.     return 0;  
  20. }  


2012-03-20 10:16:43.561 category[475:403] 

20,holydancer,0,(null),(null)

20,holydancer,0,(null),(null)

最后,不得不说说cocoa中的方法命名,一个一个方法长得,虽然很人性化很好记,不过敲起来真是麻烦啊。

objective-c中的cocoa特性:KVC-键值编码(十三)

在oc中,可以使用KVC来访问变量的属性,即使该属性没有get,set方法也可以调用,方便灵活,另外还可以方便的管理集合,具体使用我们先看代码:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. @interface Human:NSObject  
  3. {  
  4.     NSString *name;  
  5.     int age;  
  6.     Human *child;  
  7. }  
  8. //注意下面并没有写age的property  
  9. @property (copy) NSString *name;  
  10. @property (retain)Human *child;  
  11. @end  
  12. @implementation Human  
  13.   
  14. @synthesize name;  
  15. @synthesize child;  
  16. @end  
  17.   
  18. int main(int argc, const char * argv[])  
  19. {  
  20.   
  21.     @autoreleasepool {  
  22.           
  23.         Human *human = [[Human alloc]init];  
  24.         Human *child=[[Human alloc]init];  
  25.           
  26.         [human setValue:@"holydancer" forKey:@"name"];//将name属性设置为"holydancer"  
  27.         NSString *nameOfHuman=[human valueForKey:@"name"];//将human中的name属性取出  
  28.         NSLog(@"%@",nameOfHuman);  
  29.           
  30.         [human setValue:[NSNumber numberWithInt:20] forKey:@"age"];//将没有set方法的age属性赋值  
  31.         NSLog(@"%@",[human valueForKey:@"age"]);//将没有get方法的age值取出。  
  32.           
  33.         [human setValue:child forKey:@"child"];//等价于human.child=child;  
  34.           
  35.         //将human中的属性child的name属性设置为"dancer's child"          
  36.         [human setValue:@"dancer's child" forKeyPath:@"child.name"];  
  37.         //这里的方法名叫setValue:forKeyPath,可以用来设置当前对象属性的属性。  
  38.           
  39.         //将human中的属性child的age属性取出。          
  40.        NSLog(@"%@",[human valueForKeyPath:@"child.name"]);  
  41.           
  42.           
  43.           
  44.     }  
  45.     return 0;  
  46. }  

2012-03-20 19:58:30.634 kvc[3197:403] holydancer

2012-03-20 19:58:30.637 kvc[3197:403] 20

2012-03-20 19:58:30.638 kvc[3197:403] dancer's child


如上所示,例用键值编码可以很轻松地操作对象的属性和对象属性的属性。需要注意的是,因为KVC是cocoa的特性,所以在键值设置或者获取时,键统一是字符串,而值是不支持基本数据类型的,所以如上所示,我们需要将age包装成NSNumber类型,另外在输出age时,即使我们知道是int型,但取出时是按NSNumber操作的,输出占位符仍是%@.

下面我们来看看KVC的另一种简单用法,添加运算符:

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. @interface Human:NSObject  
  3. {  
  4.     NSString *name;  
  5.     int age;  
  6.     NSMutableArray *children;  
  7. }  
  8. //注意下面并没有写age的property  
  9. @property (copy) NSString *name;  
  10. @property (retain)Human *child;  
  11. @end  
  12. @implementation Human  
  13.   
  14. @synthesize name;  
  15. @synthesize child;  
  16. @end  
  17.   
  18. int main(int argc, const char * argv[])  
  19. {  
  20.   
  21.     @autoreleasepool {  
  22.           
  23.         Human *human = [[Human alloc]init];  
  24.         Human *child1=[[Human alloc]init];  
  25.         Human *child2=[[Human alloc]init];  
  26.         [child1 setValue:[NSNumber numberWithInt:5] forKey:@"age"];  
  27.         [child2 setValue:[NSNumber numberWithInt:10] forKey:@"age"];//给两个child的age属性赋值,因为没有property,所以用KVC模式赋值  
  28.         NSMutableArray *children =[[NSMutableArray alloc]init];  
  29.         [children addObject:child1];  
  30.         [children addObject:child2];  
  31.         //上面是为了将两个Human类包装为NSArray,准备放入human类  
  32.         [human setValue:children forKey:@"children"];  
  33.         NSNumber *count=[human valueForKeyPath:@"children.@count"];//利用键路径计算human对象中children属性包含的元素个数  
  34.         NSNumber *sumOfAge=[human valueForKeyPath:@"children.@sum.age"];//计算children中所有对象的年龄和  
  35.         NSNumber *maxOfAge=[human valueForKeyPath:@"children.@max.age"];//计算children中所有对象年龄最大的  
  36.         NSNumber *minOfAge=[human valueForKeyPath:@"children.@min.age"];//计算children中所有对象年龄最小的  
  37.         NSNumber *avgOfAge=[human valueForKeyPath:@"children.@avg.age"];//计算children中所有对象年龄的平均数  
  38.         //@是运算符的  
  39.         NSLog(@"children中有%@个元素,年龄和为%@,最大年龄为%@,最小年龄为%@,平均年龄为%@",count,sumOfAge,maxOfAge,minOfAge,avgOfAge);  
  40.           
  41.           
  42.           
  43.           
  44.     }  
  45.     return 0;  
  46. }  

2012-03-20 19:48:23.574 kvc[2996:403] children中有2个元素,年龄和为15,最大年龄为10,最小年龄为5,平均年龄为7.5





转自holydancer的CSDN专栏,原文地址:http://blog.csdn.net/holydancer/article/details/7366373