OC协议和分类

发布时间:2024年01月15日

前言

本篇文章介绍OC的分类和协议

分类

分类是这样一种设计,对于一个已经存在的类A,这个类可能是自己写的,或者是第三方的,甚至是系统提供的。这个类对我们很有用,但是,我们有些想要的功能在这个类里却没有实现。这个时候我们可以使用分类的功能对类A进行扩展。使用语法如下:

// 这个是A.h文件,定义了A的功能
@interface A : NSObject
{
	// 定义了一个实例变量,可以被子类使用
    int iA1;
}
@property int pA1;
-(void) A1;
@end
// 这是是分类A+B.h文件
#import "A.h"
// 分类的写法是这样的,主类名(分类名)
// 必须import主类名
@interface A(B)
{
    int iA_B1;
}
@property int pA_B1;
-(void)A_B1;
@end

分类完成后,如果我们想使用分类,必须import 分类的头文件

#import <Foundation/Foundation.h>
#import "A+B.h"// 这里需要包含分类名的头文件
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        A* a = [[A alloc] init];
        [a A_B1];// 可以看到,A的实例可以调用分类的方法
    }
    return 0;
}

注意,我们在定义分类B的时候不需要列出类A继承的类。

分类有如下特性:

  • 如果我们在分类中定义属性,分类不会自动生成访问器方法。编译器会给出警告,并且如果我们调用访问器方法,运行会出现异常
  • 如果我们需要定义只读的属性,需要在属性定义时使用readonly限定符,这样就不会警告了。
  • 分类可以使用主类在接口中定义的实例变量,但是不可以使用主类在实现中定义的实例变量,因为那是私有的。这很好理解,我们如果给系统类实现一个分类,我们总不能访问系统类的私有实例变量吧。
  • 分类不能在接口中定义实例变量,但是可以在实现中定义,仅仅当前分类使用

分类的命名

如果我们需要将分类单独保存到一个文件,通常采用A+B的形式,其中A是主类名,B是分类名

类的扩展

有一种特殊的情况是创建一个未命名的分类,实现方法就是在定义分类时在括号中不指定名字。这种特殊的语法称为类的扩展,类的扩展一般是定义在主类的实现文件中,下面是类扩展的一个例子

// A.h文件
@interface A : NSObject
{
    int iA1;
}
@property int pA1;
-(void)A1;
@end

// A.m文件
#import "A.h"
// 这是类A的扩展
@interface A()
{
    int iA_1;
}
@property int pA_1;
-(void)A_1;
@end

@implementation A
-(void)A1
{
    NSLog(@"print A1");
}

-(void)A_1
{
    NSLog(@"print A_1");
}
@end

如果我们在类的实现中定义类的扩展,扩展类有下面的属性:

  • 类的扩展中可以定义属性和实例变量,但是类的扩展中定义的属性,实例变量和方法都是私有的,只能在当前类内部访问,包括子类。通过这种方式,我们可以定义私有的东西。
  • 类的扩展中定义的方法必须在主类中实现,也就是说类扩展没有自己的实现部分
  • 不要在分类或者扩展中覆盖主类中的方法,如果需要覆盖,建议使用继承
  • 使用类扩展不仅会影响该类的所有用户,还会影响该类所有子类的用户。

协议和代理

协议的定义

协议是一组方法的接口
协议采用下面的格式进行定义

@protocol 协议名称
// 这里定义协议的接口
@required
@optional
@end

协议的使用

如果要在一个类中使用协议:

  • 首先要继承定义协议的类或者包含协议定义的头文件。
  • 并且把要遵守的协议名称放在类名和类继承的基类名称的后面,用<>括起来。
  • 如果采用多个协议,只需要将协议名都放进尖括号,用逗号分隔即可

看下面的例子:
首先是定义协议,下面的协议定义在A.h中

@protocol CALDelegate
@required
-(int)add:(int) a with:(int) b;
-(int)sub:(int) a with:(int) b;
-(int)times:(int) a with:(int) b;
-(int)div:(int) a with:(int) b;
@optional
-(int)add1:(int) a with:(int) b;
-(int)sub1:(int) a with:(int) b;
-(int)times1:(int) a with:(int) b;
-(int)div1:(int) a with:(int) b;
@end

注意:

  • @required:对于遵守协议名的类来说必须实现的方法列表
  • @optional:对于遵守协议名的类来说可以选择实现的方法列表

下面是遵守协议的类,有两种方式,一种通过继承:

@interface A1 : A<CALDelegate>

另一种通过包含A.h头文件

#import "A.h"
@interface C : NSObject<CALDelegate>

注意:协议是无类的,谁都可以遵守协议。

是否遵循协议

可以使用conformsToProtocol:方法检查一个对象是否遵循某项协议,看下面例子:

A1* a = [[A1 alloc] init];
if([a conformsToProtocol:@protocol(CALDelegate)])
{
NSLog(@"A1 conform Protocol CALDelegate");
}

注意:

  • 该方法只检查类是否遵循协议,并不代表该类实现了该协议下的方法
  • 事实上,就算你不实现任何协议定义的方法,只要遵循协议,也可以对该协议下的方法进行调用。但是运行的时候会报错,就算是@required方法,编译器也仅仅给出警告而已。

所以,一般情况下,我们如果调用协议下的方法,需要提前判断当前类是否已经实现了该方法。

代理

代理是协议的一种高级用法,通常的使用方式如下:

  • 在定义协议的类中定义一个代理类属性,该属性决定了代理类的性质和遵循的协议,看下面的例子:
    // A.h
    @protocol CALDelegate
    
    @required
    -(int)add:(int) a with:(int) b;
    -(int)sub:(int) a with:(int) b;
    -(int)times:(int) a with:(int) b;
    -(int)div:(int) a with:(int) b;
    
    @optional
    -(int)add1:(int) a with:(int) b;
    -(int)sub1:(int) a with:(int) b;
    -(int)times1:(int) a with:(int) b;
    -(int)div1:(int) a with:(int) b;
    
    @end
    @interface A : NSObject
    {
    	int iA1;
    }
    @property int pA1;
    @property   NSObject<CALDelegate>* delegate;
    -(void)A1;
    -(void)print:(int)a with:(int)b with:(int)type;
    @end
    
    其中
    @property   NSObject<CALDelegate>* delegate;
    
    这行代码就是定义代理类的属性,说明代理类需要是NSObject或者子类,并且遵循CALDelegate协议
  • 在代理类中注册
    // C.m
    @implementation C
    -(void)print
    {
    	A* a= [[A alloc] init];
    	if(!a.delegate)
    	{
    		// 代理类注册
        	a.delegate = self;
    	}
    	// 在协议类中就可以使用代理类实现的协议方法了
    	[a print:1 with:1 with:1];
    	[a print:1 with:1 with:2];
    	[a print:1 with:1 with:3];
    	[a print:1 with:1 with:4];
    }
    // 后面是对协议方法的实现
    // ...
    
文章来源:https://blog.csdn.net/b1049112625/article/details/135600048
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。