OC语言学习之——面向对象(下)

目录

一、OC的包装类

1.1 以下不是包装类:

1.2 以下是包装类:

二、处理对象

2.1 打印对象和description方法

2.2 ==和isEqual方法

 2.2.1  ==方法:

2.2.2  isEqual方法

三、类别与扩展

3.1 类别

3.1.1 利用类别进行模块化设计

3.1.2 使用类别来调用私有方法

3.2 扩展

四、协议(protocol)与委托 

4.1 协议

4.1.1 使用类别实现非正式协议

 4.1.2 正式协议的定义

4.1.3 遵守(实现)协议

4.1.4 正式协议与非正式协议的差异

4.1.5 协议与委托(delegate)


一、OC的包装类

        OC提供了NSValue、NSNumber来封装C语言基本类型(short、int、float等)。

1.1 以下不是包装类:

        1、NSInteger:大致等于long型整数

        2、NSUInteger:大致等于unsigned long型整数
        3、CGFLoat:在64位平台相当于double,在32位平台相当于float
        以上的类型只是基本类型。为了更好的兼容不同的平台,当程序需要定义整形变量的时候,建议使用NSInteger,NSUInteger;当程序需要定义浮点型变量的时候,建议使用CGFLoat。

1.2 以下是包装类:

        1、NSValue是NSNumber的父类,它代表一个更通用的包装类,可以包装int、short、long、float、char、指针、对象id等数据项。并将它们添加到NSArray、NSSet等集合中去。
        2、NSNumber是更具体的包装类,用于包装c语言的各种数值类型。它有如下三类方法:
            - [x] +numberWithXxx:直接将特定类型的值包装成NSNumber
            - [x] -initWithXxx:该实例方法需要先创建一个NSNumber对象,再用一个基本类型的值来初始化NSNumber
            - [x] -xxxValue:该实例方法返回该NSNumber对象包装的基本类型的值

        如上方法的Xxx是Int,Char,Double,string等各种数据类型。
        使用NSNumber的compare方法比较两个值,返回的对象可以转化为-1、0、1,分别代表小于、等于、大于。与bool值比较时,YES代表1,当另一个数大于1时返回1,小于1时返回-1。

        基本类型变量和包装类对象之间的转换关系可以理解为:基本类型变量通过调用numberWithXxx:类方法来转换并返回包装类对象;包装类对象通过调用xxxValue来获取基本类型的值。

        虽然OC也提供了类似于自动装箱的机制,比如可以直接把一个整型值赋给NSNumbert变量。但这种机制并不完善,使用自动装箱生成的NSNumber不支持ARC,而且不能把浮点数赋给NSNumber类型的变量。因此,通常建议显式的将基本类型的值包装成NSNumber对象。

以下代码演示了包装类的用法:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *num = [NSNumber numberWithInt:20];
        NSNumber *de = [NSNumber numberWithDouble:3.4];
        NSLog(@"%d",[num intValue]);
        NSLog(@"%g",[de doubleValue]);
        NSNumber *ch = [[NSNumber alloc] initWithChar:'j'];
        NSLog(@"%@",ch);
    }
    return 0;
}

二、处理对象

        OC对象都是NSObject子类的实例,都可以直接调用该类中定义的方法,这些方法提供了处理OC对象的通用方法。

2.1 打印对象和description方法

        NSLog()只能输出字符串,而内存中的对象要输出需要转换成字符串。当使用NSLog()函数输出对象的时候,输出的是对象的description方法的返回值。
        description方法是NSObject类的一个实例方法,所有的OC类都是NSObject类的子类,因此所有的OC类都有description方法。该方法是一个“自我描述”的方法,该方法通常用于实现这样一个功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。
        该方法返回<类:16进制的首地址>,如果用户自定义类能实现“自我描述”的功能,必须重写该方法。重写该方法总是返回该对象所有所需信息组成的字符串,格式如下:
        <类名:[实例变量1 = 值1,实例变量2 = 值2,…]>
        通过改写description方法,可以实现让系统在打印对象时打印出该对象的“自我描述”信息。实现了对该对象的自我描述。
        改写完description方法后,我们输出就可以直接写对象名了,输出的结果就是重写过的description方法中的格式化字符串。

以下代码演示了重写description方法:

接口:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKPerson : NSObject

@property (nonatomic,copy) NSString* name;
@property (nonatomic,assign) int age;

- (id) initWithName: (NSString*)name Age: (int)age;
- (NSString*) description;

@end

NS_ASSUME_NONNULL_END

实现:

#import "FKPerson.h"

@implementation FKPerson

@synthesize name;
@synthesize age;

- (id) initWithName:(NSString *)name Age:(int)age {
    if(self = [super init]) {
        self.name = name;
        self.age = age;
    }
    return self;
}
- (NSString*) description {
    //stringWithFormat可生成格式化字符串
    return [NSString stringWithFormat:@"<FKPerson [name = %@,age = %d]>",self.name,self.age];
}

@end

主函数测试及结果:

#import <Foundation/Foundation.h>
#import "FKPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKPerson *p1 = [[FKPerson alloc] initWithName:@"xuan" Age:19];
        NSLog(@"%@,%d",p1,p1);
        NSLog(@"%@,%d",p1.name,p1.age);
    }
    return 0;
}

2.2 ==和isEqual方法

        OC中测试两个变量是否相等的方式有两个,分别是:==方法和isEqual方法。

 2.2.1  ==方法:

        当用==方法时,若️①两个变量是基本类型的变量,️②两个变量都是数值型的变量(不一定要求数据类型严格相等),️③两个变量的值相等。则==判断返回真,否则返回假。
        而对于指针类型的变量,则要两个指针指向同一个对象,则==返回真,否则返回假。
        当使用==的两个类没有继承关系时,编译器会提示警告。

 @“hello”和[NSString stringWithFormat:@“hello”]的区别:
         当OC直接使用@”hello“,系统会使用常量池来管理这些字符串。常量池保证相同的字符串只会有一个,不会产生多个副本,因此创建的所有指向@“hello”的指针,指针变量保存的地址都是完全相同的。
         而使用[NSStringstringWithFormat:@“hello”]创建的字符串对象是运行时创建出来的,它被保存在运行时的内存中(即堆内存),不会放入常量池中。因此它的地址和@“hello”的地址并不相同。

以下代码演示了==的用法:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int it = 65;
        int fl = 65.0f;
        char ch = 'A';
        NSString *str1 = @"hello";
        NSString *str2 = @"hello";
        NSString *str3 = @"byebye";
        NSLog(@"%d",(it==fl)); //结果为1
        NSLog(@"%d",(fl == ch)); //结果为1
        NSLog(@"%d",(str1 == str2)); //结果为1
        NSLog(@"%d",(str2 == str3)); //结果为0
        //常量池
        NSString *p1 = @"晓美焰";
        NSString *p2 = @"晓美焰";
        NSLog(@"p1地址:%p,p2地址:%p",p1,p2);
        NSLog(@"%d",(p1 == p2)); //结果为1
        NSString *p3 = [NSString stringWithFormat:@"晓美焰"];
        NSString *p4 = [NSString stringWithFormat:@"晓美焰"];
        NSLog(@"p3地址:%p",p3);
        NSLog(@"p4地址:%p",p4);
        NSLog(@"%d",(p1 == p3)); //结果为0
        NSLog(@"%d",(p4 == p3)); //结果为0
        NSString *p5 = [NSString stringWithFormat:@"hello"];
        NSString *p6 = [NSString stringWithFormat:@"hello"];
        NSLog(@"p5地址:%p",p5);
        NSLog(@"p6地址:%p",p6);
        NSLog(@"%d",(p5 == p6)); //结果为1
    }
    return 0;
}

        要注意的是,在以上代码中,当“NSString stringWithFormat”一串汉字时,判断出来p3和p4的地址是不相同的;而“NSString stringWithFormat”一串英文时,判断出来p3和p4的地址是相同的,这是为什么呢?

        这是因为用中文创建的字符串是__NSCFString类型,运行时创建,存储在堆上,每个地址不一样。

        而英文创建的字符串是NSTaggedPointerString类型,存储在常量区,地址一样。

2.2.2  isEqual方法

        这是NSObject类提供的一个实例方法。它不严格要求两个指针变量指向同一个对象。所有指针变量都可以调用该方法来判断是否与其他指针变量相等。它要求两个指针变量指向同一个对象才返回真。
         重写isEqual方法可以自定义相等标准。
        NSString已经重写了isEqual方法,其判断两个字符串相等的标准是:只要两个字符串包含的字符序列相同,则返回真,否则返回假。
         一般而言,重写isEqual应该满足下列条件:
         1、 自反性:对任意x,[x isEqual: x]一定返回真。
         2、 对称性:对任意x和y,如果[y isEqual: x]返回真,则[x isEqual: y]也返回真。
         3、 传递性:对任意x、y、z,如果有[x isEqual: y]返回真,[y isEqual: z]返回真,则[x isEqual: z]一定返回真。
         4、 一致性:对任意x和y,如果对象中用于等价比较的关键信息没有改变,那么无论调用[x isEqual: y]多少次,返回的结果应该保持一致,要么一直是真,要么一直是假。
         5、 对任何不是nil的返回值,[x isEqual: y]一定返回假。

        在以后的iOS开发中,还会看到程序中使用isEqualToString:方法来判断两个字符串相等的代码。实际上,NSString不仅重写了isEqual:方法,用于判断两个字符串的字符序列是否相等,还定义了一个isEqualToString:方法,该方法专门用于判断当前字符串与另一个字符串的字符序列是否相等。

以下代码演示了重写isEqual的方法:

接口:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKDog : NSObject

@end

NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKItem : NSObject

@end

NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKPerson : NSObject

@property (nonatomic,copy) NSString* name;
@property (nonatomic,copy) NSString* idStr;

- (id) initWithName: (NSString*) name idStr: (NSString*) idStr;

@end

NS_ASSUME_NONNULL_END

实现:

#import "FKDog.h"

@implementation FKDog

- (BOOL) isEqual:(id)other {
    //不进行判断,直接返回YES,即FKDog与任何对象都相等
    return YES;
}

@end
#import "FKItem.h"

@implementation FKItem

@end
#import "FKPerson.h"

@implementation FKPerson

- (id) initWithName: (NSString*) name idStr: (NSString*) idStr {
    if(self = [super init]) {
        self.name = name;
        self.idStr = idStr;
    }
    return self;
}
- (BOOL) isEqual:(id) other {
    //如果两个对象为同一个对象
    if(self == other) {
        return YES;
    }
    //当other不为nil且它为FKPerson的实例时
    if(other != nil && [other isMemberOfClass:FKPerson.class]) {
        FKPerson* target = (FKPerson*)other;
        //并且要判断当前对象的idStr和target对象的idStr相等才可以判断两个对象相等
        return [self.idStr isEqual: target.idStr];
    }
    return NO;
}

@end

主函数测试及结果:

#import <Foundation/Foundation.h>
#import "FKDog.h"
#import "FKItem.h"
#import "FKPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKDog *p1 = [[FKDog alloc] init];
        NSLog(@"FkDog对象是否isEqual FKItem对象?%d",[p1 isEqual:[FKItem new]]);
        NSLog(@"FKDog对象是否isEqual NSString对象?%d",[p1 isEqual:[NSString stringWithFormat:@"hello"]]);
        FKPerson* s1 = [[FKPerson alloc] initWithName:@"晓美焰" idStr:@"12341234"];
        FKPerson* s2 = [[FKPerson alloc] initWithName:@"鹿目圆" idStr:@"12341234"];
        FKPerson* s3 = [[FKPerson alloc] initWithName:@"巴麻美" idStr:@"345676545678"];
        NSLog(@"s1和s2是否相等?%d",[s1 isEqual: s2]);
        NSLog(@"s2和s3是否相等?%d",[s2 isEqual: s3]);
    }
    return 0;
}

 

三、类别与扩展

        在iOS的开发过程中,经常会有需要扩展一个新方法的情况,对于这种扩展,我们首先想到的可能是继承,但是有时候继承并不是最好的选择,比如希望为NSNumber类新增一些方法,但是NSNumber只是一个类簇的前端类,例如,通过[NSNumber numberWithInt: 5]方法所生成的NSNumber对象其实只是NSNumber子类的实例。这样即使为NSNumber派生子类也没有意义,派生的子类对NSNmuber现有的子类并没有任何影响。此时我们就可以用类别来实现。

        类簇:定义一个父类,并且以该父类派生多个子类,其他程序使用这些类时,总是面向父类编程,当程序调用父类的初始化方法、类方法来返回对象时,实际上返回的是子类的实例。这一系列的类被称为一个类簇,这个父类也就模拟了抽象类的作用。

        类簇属于面向对象框架向外公布接口的一种实现的手段,作用是简化框架的接口,并且不影响功能的丰富性。

3.1 类别

        通过使用类别即可动态的为现有的类添加新方法,而且可以将类模块化地分布到多个相关文件中。

        类别的定义:

        命名规则:在接口部分的文件命名是“类名+类别名.h”,在实现部分的文件命名是“类名+类别名.m”的形式。

        类别的接口部分的声明和类的定义十分相似,但类别不继承父类,只需要在已有类的类名后面加一个括号,写入类别名,然后再在下面定义方法。

@interface 已有类 (类别名) 

... //方法

@end

        实现部分也是一样

@implementation 已有类 (类别名) 

... //方法

@end

        虽然类别可以重写原有类中的方法,但是通常并不推荐这么做,如果需要重写原有类的方法,建议是通过原有类为父类来派生一个子类,然后在子类中重写父类的方法。

        一些注意:

        1、在类别定义的方法,会成为原始类的一部分,与其他方法的调用没有区别。

        2、通过给父类定义类别的方法,其所有子类都会继承这些方法。如果子类添加类别方法,父类则不会拥有子类的类别方法。

以下代码演示了类别的用法:

接口:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKPerson : NSObject

@property (nonatomic,copy) NSString* name;
@property (nonatomic,assign) int age;

- (void) test1;

@end

NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "FKPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface FKPerson (test2)

- (void) dda;

@end

NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSNumber (fk)

- (NSNumber*) add: (double) num2;
- (NSNumber*) substract: (double) num2;
- (NSNumber*) multiply: (double) num2;
- (NSNumber*) divide: (double) num2;

@end

NS_ASSUME_NONNULL_END

实现:

#import "FKPerson.h"

@implementation FKPerson

- (void) run {
    NSLog(@"test1");
}

@end
#import "FKPerson+test2.h"


@implementation FKPerson (test2)

- (void) dda {
    NSLog(@"test2");
}

@end
#import "NSNumber+fk.h"

@implementation NSNumber (fk)

- (NSNumber*) add: (double) num2 {
    return [NSNumber numberWithDouble:([self doubleValue] + num2)];
}
- (NSNumber*) substract:(double) num2 {
    return [NSNumber numberWithDouble:([self doubleValue] - num2)];
}
- (NSNumber*) multiply:(double) num2 {
    return [NSNumber numberWithDouble:([self doubleValue] * num2)];
}
- (NSNumber*) divide:(double) num2 {
    return [NSNumber numberWithDouble:([self doubleValue] / num2)];
}

@end

主函数测试及结果:

#import <Foundation/Foundation.h>
#import "NSNumber+fk.h"
#import "FKPerson+test2.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *myNum = [NSNumber numberWithInt:3];
        NSNumber *add = [myNum add:2.4];
        NSLog(@"%@",add);
        NSNumber *substract = [myNum substract:2.4];
        NSLog(@"%@",substract);
        NSNumber *multiply = [myNum multiply:2.4];
        NSLog(@"%@",multiply);
        NSNumber *divide = [myNum divide:2.4];
        NSLog(@"%@",divide);
        
        FKPerson *myTest = [[FKPerson alloc] init];
        [myTest dda];
    }
    return 0;
}

         接下来来说一些类别的作用:

3.1.1 利用类别进行模块化设计

        类的实现部分不能分布到多个.m文件中去,因此当某个类非常大时,会导致那个类实现所在的文件非常大,以至于维护起来非常困难。因此如果需要将一个较大的类分模块设计,使用类别是一个不错的选择。通过类别可以对类实现按模块分布到不同的*.m文件中,从而提高项目后期的可维护性。

3.1.2 使用类别来调用私有方法

        在前面的学习中我们知道,在实现部分定义的方法相当于私有方法,通常不允许调用。但是实际上私有方法仍然有办法调用。比如这一小节要学的,通过类别来定义前向引用,从而实现对私有方法的调用。

我们用代码来演示如何调用:

首先,我们定义一个FKItem类的接口部分,它只声明了一个info方法。

#import <Foundation/Foundation.h>

@interface FKItem : NSObject

@property (nonatomic,assign) double price;
- (void) info;

@end

然后我们再在实现部分新添加一个方法,相当于私有方法

#import "FKItem.h"

@implementation FKItem 

@synthesize price;
- (void) info {
    NSLog(@"这是一个普通的方法")
}
//新增的私有方法
- (double) calDiscount: (double) discount {
    return self.price *discount;
}

@end

这时候我们再到主函数中直接调用calDiscount方法时,会报错说没有这个方法。这要如何解决呢,就要用到类别的知识了。我们可以在main()函数下新建一个类别,然后再在类别中声明calDiscount方法,此时就可以了。

#import <Foundation/Foundation.h>
#import "FKItem.h"

@interface FKItem (fk)

- (double) calDiscount: (double)diacount;

@end

int main(int argc,char *argv[]) {
    @autoreleasepool {
        FKItem *item = [[FKItem alloc] init];
        item.price = 109;
        [item info];
        NSLog(@"物品打折的价格是:%g",[item calDiscount:.75]);
    }
}

3.2 扩展

        扩展与类别相似,扩展相当于匿名类别,定义扩展的语法格式如下:

@interface 已有类 () {
    实例变量
}

... //方法定义

@end

        从语法上来看,扩展相当于定义一个匿名的类别,但从用法来看,类别一般是有特定的.h和.m文件,扩展则用于临时对某个类的接口进行扩展,类实现部分同时实现类接口部分定义的方法和扩展中定义的方法。

        扩展和类别的不同点还有就是:定义类的扩展的时候,可以额外增加实例变量,也可以用@property来合成属性(包括setter和getter方法和对应的成员变量),但定义类的类别的时候,是不允许额外定义实例变量和合成属性的。

接下来用代码来演示一下如何扩展类:

首先先创建一个FKCar类别的接口:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKCar : NSObject

@property (nonatomic,copy) NSString *brand;
@property (nonatomic,copy) NSString *model;

- (void) drive;

@end

NS_ASSUME_NONNULL_END

然后对它的属性和方法进行扩展,扩展的接口文件是这个:

#import <Foundation/Foundation.h>
#import "FKCar.h"

NS_ASSUME_NONNULL_BEGIN

@interface FKCar ()

@property (nonatomic,copy) NSString *color;
- (void) drive: (NSString*) owner;

@end

NS_ASSUME_NONNULL_END

然后是FKCar的实现部分:

#import "FKCar.h"
#import "FKCar+drive.h"

@implementation FKCar

@synthesize brand;
@synthesize model;
@synthesize color;

- (void) drive {
    NSLog(@"汽车正在路上跑");
}
- (void) drive: (NSString*) owner {
    NSLog(@"%@正驾驶着%@汽车在路上跑",owner,self);
}
- (NSString*) description {
    return [NSString stringWithFormat:@"<FK[brand = %@,model = %@,color = %@",self.brand,self.model,self.color];
}

@end

主函数测试及结果:

#import <Foundation/Foundation.h>
#import "FKCar+drive.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKCar *car = [[FKCar alloc] init];
        car.brand = @"宝马";
        car.model = @"X5";
        car.color = @"黑色";
        [car drive];
        [car drive: @"孙悟空"];
    }
    return 0;
}

四、协议(protocol)与委托 

4.1 协议

        类是一种具体实现体,而协议则是定义了一种规范,定义了某一批类所需要遵守的规范。协议不提供任何实现,它体现的是规范和实现分离的设计哲学。

        让规范和实现分离正是协议的好处,是一种松耦合的设计。

4.1.1 使用类别实现非正式协议

        这也是类别的作用之一。

        当某个类实现NSObject的该类别时,就需要实现该类别下所有方法,这种基于NSObject定义的类别即可认为是非正式协议。

以下用代码来演示非正式协议:

首先我们创建一个NSObject的类别,名为EaTable,并且为其定义一个taste方法:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (EaTable)

- (void) taste;

@end

NS_ASSUME_NONNULL_END
#import "NSObject+EaTable.h"

@implementation NSObject (EaTable)

@end

然后为NSObject派生一个子类FKApple,这个子类虽然只是一个空类,但并不影响它的功能,因为它继承了NSObject(EaTable),只要在FKApple类的实现部分实现taste方法即可。

接口:

#import <Foundation/Foundation.h>
#import "NSObject+EaTable.h"

NS_ASSUME_NONNULL_BEGIN

@interface FKApple : NSObject

@end

NS_ASSUME_NONNULL_END

实现:

#import "FKApple.h"

@implementation FKApple

- (void) taste {
    NSLog(@"苹果营养丰富,口味很好");
}

@end

主函数测试及结果:

#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKApple *app = [[FKApple alloc] init];
        [app taste];
    }
    return 0;
}

 4.1.2 正式协议的定义

        定义正式协议的时候,不再使用@interface和@implementation关键字了,而是使用@protocol关键字,定义正式协议的语法如下:

@protocol 协议名 <父协议1,子协议2> {
    零到多个方法定义......
}

注意:

        1、协议名应与类名采用相同的命名规则。即协议名应该由多个有意义的单词连接而成,每个单词首字母大写。

        2、一个协议可以有多个直接父协议,但协议只能继承协议,不能继承类。

        3、协议中定义的方法只有方法签名,没有方法实现,协议中包含的方法即可以是类方法也可以是实例方法。

        4、协议中所有方法都是公开的访问权限。

以下是三个定义正式协议的代码:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@protocol FKOutput

- (void) output;
- (void) addData(String msg);

@end

NS_ASSUME_NONNULL_END

上面定义了一个FKOutput协议,这个协议定义了两个方法,分别表示添加数据和输出数据。

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@protocol FKProoductable

- (NSData*) getProductTime;

@end

NS_ASSUME_NONNULL_END

以上定义了一个FKProductable协议,其中定义了一个getProductTime方法,用来返回产品的生产时间。

#import <Foundation/Foundation.h>
#import "FKOutput.h"
#import "FKProductable.h"

NS_ASSUME_NONNULL_BEGIN

@protocol FKPrintable <FKOutput,FKProductable>

- (NSString*) printColor;

@end

NS_ASSUME_NONNULL_END

上面这个是一个打印机协议,该协议同时继承以上两个协议。

        协议的继承和类的继承不一样,协议完全支持多继承,即一个协议可以有多个直接的父协议。和类继承相似,子协议继承某个父协议,将会获得父协议中的所有方法。

        一个协议继承多个父协议时,多个父协议排在<>中间,多个协议口见以(,)隔开。

4.1.3 遵守(实现)协议

        在类定义的接口部分可以指定该类继承的父类,以及遵守的协议,语法如下:

@interface 类名: 父类<协议1,协议2...>

        由上面的语法格式可以看出,一个类可以同时遵守多个协议。

        如果程序需要使用协议来定义变量,有如下两种语法:

        1、NSObject<协议1,协议2...>* 变量;

        2、id<协议1,协议2...>* 变量;

        通过上面的语法格式定义的变量,它们的编译时的类型仅仅只是所遵守的协议类型,因此只能调用该协议中定义的方法。

4.1.4 正式协议与非正式协议的差异

        1、非正式协议通过为NSObject创建类别来实现,而正式协议直接使用@protocol创建;

        2、遵守非正式协议通过继承带特定类别的NSObject来实现,而遵守正式协议则有专门的OC语法来实现;

        3、遵守非正式协议不要求实现协议中定义的所有方法;而遵守正式协议则必须实现协议中定义的所有方法。

        在OC中还有两个关键字:

        1、@optional:位于该关键字只后、@required或@end之前声明的方法是可选的,实现类可选择是否实现这些方法。

        2、@required:位于该关键字之后、@optional或@end之前声明的方法是必需的,实现类必需实现这些方法。

        通过在正式协议中使用以上两个关键字,正式协议完全可以代替非正式协议的功能。

4.1.5 协议与委托(delegate)

        定义协议的类可以把协议定义的方法委托给实现协议的类,这样可以让类定义具有更好的通用性质。

        更通用的,当应用程序启动时,应用程序启动的开始加载、加载完成等系列事件,都是委托给相应的代理对象完成的。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值