OC学习7——类别、扩展和协议

时间:2022-04-29
本文章向大家介绍OC学习7——类别、扩展和协议,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1、我么在调用NSLog()方法打印一个对象时,实际上是调用了该对象的description方法,这个description方法就和Java中的toString()方法一样。所以,下面两行代码其实是一样的

NSLog(@"%@", p) ; 

NSLog(@"%@", [p description]) ;

description方法是NSObject类的一个实例方法,所有的OC都是NSObject类的子类,因此,所有的类都有description方法。description方法方法通常用于实现这样的一个功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。NSObject类提供的description方法总是返回<FKPerson:十六进制的首地址>,这个返回值并不是先自我描述的功能,因此,如果用户需要自定义类实现自我描述的功能,则必须重写NSObject类的description方法。

2、==与isEqual()方法的区别(这一点与Java中的异同点是一致的):

  • ==:如果是比较对象是两个基本类型,则数值相同就返回true,不同则返回false。如果比较对象是两个指针变量,则如果两个指针都指向同一个对象则返回true,否则返回false。
  • isEqual():该方法是NSObject类提供的一个实例方法,因此所有指针变量都可以调用这个方法来判断是否与其他指针变量相等。在默认情况下,isEqual()的判断方法和==一样。如果希望采用自定义的相等标准,则可以通过重写isEqual()方法来实现。NSString已经重写了isEqual()方法,NSString的isEqual()方法判断两个字符串相等的标准是:只要两个字符串包含的所有的字符序列相同,则isEqual()返回true,否则返回false。

3、OC中没有像Java和C++那样提供抽象类的语法支持,而在实际项目开发中,总有需要用到抽象类的时候,此时就会选择定义一个父类,并以该父类派生出多个子类,其他程序使用这些类时,实际上返回的是子类的实例,这一系列的类被称为一个类簇(cluster),这个父类就模拟了抽象类的功能。

OC的动态特性允许使用类别(category)为现有的类添加新房,并且不需要创建子类,不需要访问原有类的代码。通过使用类别就可以动态地为现有的类添加新方法,而且可以将类定义模块化地分不到多个文件中。类别同样由接口和实现部分组成,接口和实现部分语法格式如下:

@interface 已有类 (类别名)
//方法定义,类别中只能添加方法,不能添加成员变量
。。。
@end

@implementation已有类 (类别名)
//方法实现
。。。
@end
  • 类别名的命名很随意,我们自己取定就可以,一般用于描述增加部分的功能
  • 通过类别为制定的类添加新方法之后,这个新方法不仅会影响到该类,还会影响到该类的所有子类,每个子类都会获得类别扩展的方法。
  • 可根据需要为一个类定义多个类别,不同的类别都可对原有的类增加方法的定义。
  • 就编程习惯而言,一般习惯将类别的接口文件命名为“类名+类别名.h”的形式,同样,实现文件则命名位“类名+类别名.m”的形式。
//接口部分
#import <Foundation/Foundation.h>

@interface NSArray (Convert)

+(NSMutableArray *)arrayFormNumber:(int)number;

@end

//实现部分
#import "NSArray+Convert.h"

@implementation NSArray (Convert)

+(NSMutableArray *)arrayFormNumber:(int)number{
    NSMutableArray *numberArray=[[NSMutableArray alloc]init];
    while (number) {
        int last=number%10; //取出最后一位;
        number=number/10;
        [numberArray addObject:[NSNumber numberWithInt:last]];       
    }
    return  [numberArray autorelease];
}

@end
  • 使用类别可以对类进行模块化设计:在前面类的设计中,类的接口部分在.h文件中定义,类的实现部分在.m文件中定义,且类的实现不能分布到多个.m文件中。但是当某个类特别大时,如果将所有实现都放在一个.m文件中,将会导致这个文件非常大,以至于维护起来非常困难。如果将一个较大的类进行分模块设计,使用类别是一个不错的选择。例如NSWindow类就采用这种设计思想,具体可以在xcode上查看NSWindow.h文件。
  • 使用类别来实现私有方法的调用:OC中实际上并没有真正的是有方法,通常而言所说的私有方法指的是没有在接口部分定义而在实现部分定义的方法,这类方法是不允许被调用的,因为没有通过接口部分向外暴露调用接口。在OC中,除了通过NSObject类的performSelector()来执行动态调用从而实现调用私有方法之外,我们还可以通过使用类别来定义前向引用(其实就是通过类别在接口部分补充定义之前未定义的私有方法),从而实现对私有方法的调用。

4、OC中的扩展(extension)和类别相似,扩展相当于匿名类别,不同的是扩展可以定义实例变量。定义扩展的语法格式如下:

@interface 已有类 ()
{
    //定义实例变量    
}
//方法定义
。。。
@end
  • 在语法上,扩展相当于匿名类别。但是在用法上,类别通常是有单独的.h和.m文件,而扩展则用于临时对某一个类的接口进行扩展,类实现部分同时实现类接口部分和扩展中定义的方法。
  • 在类的扩展中,可以额外增加实例变量,也可以用@property来合成属性,但是在定义类别是则不允许定义成员变量,也不能用@property合成属性。
 1 #import "vehicle.h"
 2 @interface Vehicle ()
 3 @property(nonatomic, strong) NSString *color;
 4 -(void) drive:(NSString *)name;
 5 @end
 6 
 7 //在实际使用中,通用的做法是省略掉Vehicle_ext.h文件,而是将其中的内容直接添加在Vehicle.m文件@implementation前部即可
 8 #import "Vehicle_ext.h"
 9 @implementation Vehicle
10 -(void) drive:(NSString *)name
11 {
12     NSLog(@"交通工具名称%@,颜色属性%@",_name,_color);
13 }
14 @end

5、协议(protocol)是OC的一个重要知识点,其作用类似于Java中的接口,用于定义多个类应该遵循的规范。协议提提供任何实习那,协议体现的是规范和实现分离的松耦合的设计哲学。

  协议定义的是多个类共同的公共行为规范,这些行为是与外部交流的通道,这就意味着协议里通常是定义一组公用方法,但是不会为这些方法提供实现,方法的实现则交给类去完成。协议定义时使用@protocol关键字,在协议中还有两个关键字@optional and @required,@optional 声明的方法可以实现,也可以不实现。@required声明的方法必须实现。具体语法格式如下:

// 定义的协议名称 遵守协议名称
@protocol MyProtocol <NSObject>

@required
//定义必选方法

@optional
//定义可选方法

@end


//使用协议
@interface 类名 :父类 <协议1,协议2 ... >
  • 一个协议可以有多个直接父协议,但协议只能继承协议,不能继承类
  • 学一种定义的方法只有方法签名,没有实现。协议中的方法既可以是类方法,也可以是实例方法。
 1 //
 2 //  Women.h
 3 //  正式协议的定义
 4 //
 5 //  Created by Goddog on 15/1/11.
 6 //  Copyright (c) 2015年 Goddog. All rights reserved.
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 
11 @protocol Women <NSObject>
12 //定义协议的方法
13 @optional   //可以不实现该接口的方法
14 -(void) pretty;
15 @required   //必须实现该接口的方法
16 -(void) beautiful:(NSString*) count;
17 @end
 1 //
 2 //  Man.h
 3 //  正式协议的定义
 4 //
 5 //  Created by Goddog on 15/1/11.
 6 //  Copyright (c) 2015年 Goddog. All rights reserved.
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 
11 @protocol Man <NSObject>
12 //定义协议的方法
13 -(void) handsome;
14 @end
 1 //
 2 //  Person.h
 3 //  正式协议的定义
 4 //
 5 //  Created by Goddog on 15/1/11.
 6 //  Copyright (c) 2015年 Goddog. All rights reserved.
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 #import "Women.h"
11 #import "Man.h"
12 
13 //协议继承了Women、Man协议
14 @protocol Person <Women,Man>
15 //定义协议的方法
16 -(NSString*) play;
17 @end
 1 //
 2 //  SuperMan.h
 3 //  正式协议的定义
 4 //
 5 //  Created by Goddog on 15/1/11.
 6 //  Copyright (c) 2015年 Goddog. All rights reserved.
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 #import "Person.h"
11 
12 @interface SuperMan : NSObject<Person>  //实现Person的协议
13 
14 @end
 1 //
 2 //  SuperMan.m
 3 //  正式协议的定义
 4 //
 5 //  Created by Goddog on 15/1/11.
 6 //  Copyright (c) 2015年 Goddog. All rights reserved.
 7 //
 8 
 9 #import "SuperMan.h"
10 #define MAX_CACHE_LINE 10
11 
12 @implementation SuperMan
13 {
14     NSString* playData[MAX_CACHE_LINE];   //使用数组记录所有需要缓存的数据
15     int dataNum;     //记录当前的数量
16 }
17 
18 //实现协议方法
19 -(void) pretty
20 {
21     //只要还有漂亮的,继续上
22     while (dataNum > 0) {
23         NSLog(@"正在和%@玩%@",playData[0],[self play]);
24         //剩下的人数减少
25         dataNum --;
26         //把列队整体向前移
27         for (int i = 0; i < dataNum; i++) {
28             playData[i] = playData[i + 1];
29         }
30     }
31 }
32 
33 //实现协议方法
34 -(void) beautiful:(NSString *)count
35 {
36     if (dataNum >= MAX_CACHE_LINE) {
37         NSLog(@"人数已满,不要在上了!");
38     }
39     else
40     {
41         //把人数添加到列队中
42         playData[dataNum++] = count;
43     }
44 }
45 
46 //实现协议方法
47 -(void) handsome
48 {
49     NSLog(@"英俊");
50 }
51 
52 //实现协议方法
53 -(NSString*) play
54 {
55     
56     return @"游戏";
57 }
58 
59 @end
 1 #import <Foundation/Foundation.h>
 2 #import "SuperMan.h"
 3 
 4 int main(int argc, const char * argv[]) {
 5     @autoreleasepool {
 6             //创建超人对象
 7         SuperMan* superMan = [[SuperMan alloc] init];
 8         //调用协议的方法
 9         [superMan beautiful:@"乌克兰美女"];
10         [superMan beautiful:@"白俄罗斯美女"];
11         [superMan pretty];
12         [superMan handsome];
13         
14         //创建超人对象,当成Man使用
15         NSObject<Man>* man = [[SuperMan alloc] init];
16         //调用Man协议中定义的方法
17         [man handsome];
18         
19         //创建超人对象,当成Women使用
20         id<Women> women = [[SuperMan alloc] init];
21         //调用Women协议中定义的方法
22         [women beautiful:@"俄罗斯美女"];
23         [women pretty];
24         //[women handsome];//这样是不能调用的
25     }
26     return 0;
27 }