1、类的继承

我们说Objective-C也是一门面向对象的语言,因此也具有面向对象语言的基本特征.在Objective-C的继承体系中,位于最顶层的根类是NSObject,类比Java中的java.lang.Object类,我们定义的所有类都是它的子类。我们知道在Java中继承一个类需要extends关键字,在Objective-C中,继承一个类只需要一个冒号(:),语法如下:

06_extends_0
06_extends_0

继承使得子类可以从父类中获得一些属性和已有方法。例如我们之前已经定义一个手机类(Phone),现在我们再来定义一个iPhone手机类(IphonePhone)和Android手机类(AndroidPhone),都继承至手机类(Phone),在IphonePhone和AndroidPhone中我们不写任何代码. Phone.h

#import  <Foundation/Foundation.h>

@interface Phone : NSObject

@property NSString *color;
@property NSString *brand;
@property NSString *os;

-(void) makeCall:(NSString*) number;
-(void) sendMsg:(NSString*) msg andNumber:(NSString*) number;

@end

Phone.m

#import "Phone.h"

@implementation Phone

@synthesize color;
@synthesize brand;
@synthesize os;

-(void) sendMsg:(NSString*) msg andNumber:(NSString*) number{
    NSLog(@"发送短信给 %@, 短信内容:%@",number,msg);
}

-(void) makeCall:(NSString*) number{
    NSLog(@"正在呼叫: %@",number);
}
@end

IphonePhone.h

#import "Phone.h"
@interface IphonePhone : Phone
@end

IphonePhone.m

#import "IphonePhone.h"

@implementation IphonePhone

@end

AndroidPhone.h

#import "Phone.h"
@interface AndroidPhone : Phone
@end

AndroidPhone.m

#import "AndroidPhone.h"
@implementation AndroidPhone
@end

然后我们在main.m中实例化IphonePhone和AndroidPhone,我们发现我们可以访问定义在Phone中的变量和方法:

#import  <Foundation/Foundation.h>
#import "Phone.h"
#import "IphonePhone.h"
#import "AndroidPhone.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        IphonePhone* iphone = [[IphonePhone alloc] init];

        iphone.os = @"ios";
        iphone.color = @"black";
        iphone.brand = @"iPhone";

        NSLog(@"iphone.os:%@\niphone.color:%@\niphone.brand:%@\n",iphone.os
              ,iphone.color,iphone.brand);

        [iphone makeCall:@"10086"];
        [iphone sendMsg:@"10086" andNumber:@"我每个月的流量怎么这么多,你们是不是有暗扣?"];

        AndroidPhone* android  = [[AndroidPhone alloc] init];
        android.os = @"Android";
        android.color=@"multicolour";
        android.brand = @"HuaWei";

        NSLog(@"android.os=%@\nandroid.color=%@\nandroid.brand=%@",android.os
              ,android.color,android.brand);
        [android makeCall:@"10086"];
        [android sendMsg:@"10086" andNumber:@"我每个月的话费这么多,你们暗扣了吧?"];
    }
    return 0;
}

我们在实例化IphonePhone时,IphonePhone* iphone = [[IphonePhone alloc] init];  alloc和init我们并没有定义,事实是alloc和init是NSObject中定义的方法,并且alloc是类方法,init是实例方法.

和Java一样,Objective-C中每个子类只能有一个超类,也就是说ClassC不能同时继承ClassA和ClassB,这点和C++不一样.但Objective-C允许多层继承,例如,ClassB继承ClassA,ClassC又继承ClassB,那么我们也说ClassC是ClassA的子类.

2、@class指令

到目前为止,当一个类需要引用另一个类,我们通使用import 类名,例如在AndroidPhone.h和IphonePhone.h都继承至Phone,所以我们需要import Phone.h,有时候我们在一个类中引用另一个类,并不是为了继承,而ClassB是ClassA的一个属性.为了说明这个问题,我们把Phone中原先的os属性抽象成一个类(OS),比如os也有版本名和版本号两个属性:

#import  <Foundation/Foundation.h>

@interface OS : NSObject

   @property NSString* versionName;
   @property int versionCode;

@end

然后我们需要修改Phone.h中的代码:

#import  <Foundation/Foundation.h>
@class OS; //使用@class指令引入OS类
@interface Phone : NSObject
@property NSString *color;
@property NSString *brand;
@property OS *os;  //这里的os不再是NSString 而是一个类

-(void) makeCall:(NSString*) number;
-(void) sendMsg:(NSString*) msg andNumber:(NSString*) number;

@end

我们看第三行使用了@class指令,这是因为编译器在遇到定义的实例变量OS时,必须了解OS是什么.当然我们也可以使用#import “OS.h”代替这条指令.但是使用@class指令提高了效率.因为h文件在修改后,所有import该h文件的所有文件必须重新build,因此,如果把#import写在h文件中,import该h文件的文件也就会产生不必要的编译.在这里,编译器不需要引入和处理整个OS.h文件,虽然它很小,只需要知道OS是一个类名. 但是如果我们需要使用一个类的方法(在实现部分),使用@class指令是不够的,因为编译器需要更多的消息.它需要清楚方法有多少参数,参数的类型,以及方法的返回类型等.

二者的区别在于:

  1. import会包含这个类的所有信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。
  2. 在头文件中, 一般只需要知道被引用的类的名称就可以了。 不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。
  3. 在编译效率方面考虑,如果你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–> B, B–> C, C–> D这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,如果你的类有很多的话,这将耗费大量的时间。而是用@class则不会。
  4. 如果有循环依赖关系,如:A–> B, B–> A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会有编译错误出现。

所以,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。 在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要import在@class中声明的类进来.

3、覆盖父类方法(方法重写,override)

在Objective-c中,子类可继承父类中的方法,而不需要重新编写相同的方法,直接可以使用父类的方法。 但有时我们不想使用使用父类方法,而是想作一定的修改,怎么办呢?只要将子类中书写一个与父类具有相同的方法名、返回类型和参数,就可以将将父类的方法覆盖重写。这点与Java一样,比较简单.

比如下面IphonePhone中的makeCall:方法就重写了Phone中的makeCall方法.

#import "IphonePhone.h"

@implementation IphonePhone

-(void) makeCall:(NSString *)number{
    NSLog(@"我在使用iPhone呼叫:%@",number);
}

@end

NSObject类所支持的一些基本方法。

-(BOOL) isKindOf:class-object(判断对象是否是class-object或其子类的成员)

-(BOOL) isMenberOfClass:class-object(判断对象是否是class-object的成员)

-(BOOL) respondsToSelector:selector(判断对象是否能够响应selector所指定的方法)

+(BOOL) instancesRespondToSelector:selector(判断指定的类实例是否能响应selector所指定的方法)

+(BOOL) isSubclassOfClass:class-object(判断对象是否是指定类的子类)

-(id) performSelector:selector(应用selector指定的方法)

-(id) performSelector:selector withObject:object(应用selector指定的方法,传递参数object)

-(id) performSelector:selector withObject:object1 withObject:object2(应用selector指定的方法,传递参数object1和object2)