30天学会OC-10:协议与分类

学习目标

  • 理解协议(Protocol)的概念及其在Objective-C中的作用。
  • 掌握如何定义协议、实现协议和检查协议实现。
  • 理解分类(Category)的概念及其在Objective-C中的作用。
  • 掌握如何创建分类和使用分类扩展已有类的功能。
  • 了解分类与子类的区别。

学习内容

1. 协议(Protocol)

协议是Objective-C中定义方法列表的一种方式,它声明了一组方法,但不提供实现。任何类都可以选择实现协议中的方法。协议类似于Java中的接口或C++中的纯虚类。

1.1 协议的定义

协议使用 @protocol 关键字定义,语法如下:

@protocol ProtocolName
// 方法声明
@required // 必须实现的方法(默认)
- (void)requiredMethod;

@optional // 可选实现的方法
- (void)optionalMethod;
@end
  • @required:标记的方法必须由实现该协议的类实现(默认)。
  • @optional:标记的方法可以选择性实现。

示例:

// 定义一个名为Printable的协议
@protocol Printable
- (void)print;
@optional
- (NSString *)description;
@end
1.2 协议的实现

类通过 <ProtocolName> 语法声明实现协议,并在类中实现协议中的方法:

@interface ClassName : SuperClass <Protocol1, Protocol2>
// 类定义
@end

示例:

// Document.h
@interface Document : NSObject <Printable>
@property (nonatomic, copy) NSString *title;
@end

// Document.m
@implementation Document
- (void)print {
    NSLog(@"Printing document: %@", self.title);
}

// 可选实现
- (NSString *)description {
    return [NSString stringWithFormat:@"Document: %@", self.title];
}
@end
1.3 协议的使用

可以通过 conformsToProtocol: 方法检查对象是否实现了某个协议:

id obj = [[Document alloc] init];
if ([obj conformsToProtocol:@protocol(Printable)]) {
    // 对象实现了Printable协议
    if ([obj respondsToSelector:@selector(print)]) {
        [obj print];
    }
}
1.4 协议作为类型

协议可以作为类型使用,表示任何实现了该协议的对象:

- (void)printObject:(id<Printable>)printableObject {
    [printableObject print];
}
1.5 协议继承

协议可以继承其他协议:

@protocol AdvancedPrintable <Printable>
- (void)printWithColor:(UIColor *)color;
@end

2. 分类(Category)

分类是Objective-C中一种强大的机制,允许在不修改原始类的情况下,为现有类添加新的方法。

2.1 分类的定义

分类使用 @interface 关键字定义,语法如下:

@interface ClassName (CategoryName)
// 方法声明
@end

示例:

// NSString+Utilities.h
@interface NSString (Utilities)
- (BOOL)isValidEmail;
- (NSString *)reverseString;
@end

// NSString+Utilities.m
@implementation NSString (Utilities)
- (BOOL)isValidEmail {
    // 简单的邮箱验证
    NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}";
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
    return [emailTest evaluateWithObject:self];
}

- (NSString *)reverseString {
    NSMutableString *reversedString = [NSMutableString string];
    NSInteger charIndex = [self length];
    while (charIndex > 0) {
        charIndex--;
        NSRange subStrRange = NSMakeRange(charIndex, 1);
        [reversedString appendString:[self substringWithRange:subStrRange]];
    }
    return reversedString;
}
@end
2.2 分类的使用

分类中的方法会成为原始类的一部分,可以直接调用:

NSString *email = @"test@example.com";
if ([email isValidEmail]) {
    NSLog(@"Valid email");
}

NSString *original = @"Hello";
NSString *reversed = [original reverseString]; // olleH
2.3 分类的注意事项
  1. 不能添加实例变量:分类只能添加方法,不能添加实例变量或属性(但可以使用关联对象实现类似功能)。
  2. 方法名冲突:如果分类中的方法与原始类或其他分类中的方法同名,调用哪个方法是不确定的。
  3. 覆盖原始方法:虽然技术上可以覆盖原始类的方法,但不推荐这样做,因为无法调用原始实现。
2.4 分类与子类的区别
特性分类子类
添加方法可以可以
添加实例变量不可以可以
覆盖方法可以(不推荐)可以
调用原始实现不可以可以(通过super)
适用范围所有该类的实例仅子类实例

3. 类扩展(Class Extension)

类扩展是一种特殊的分类,没有名称(匿名分类),通常用于:

  1. 声明私有方法和属性
  2. 重新声明可读写属性
// Person.m
@interface Person ()
@property (nonatomic, copy) NSString *privateID;
- (void)privateMethod;
@end

@implementation Person
// 实现
@end

练习

  1. 定义一个 Drawable 协议,包含 draw 方法(必须实现)和 erase 方法(可选实现)。创建 CircleRectangle 类实现该协议。
  2. NSArray 创建一个分类,添加 sum 方法(计算所有 NSNumber 元素的和)和 average 方法(计算平均值)。
  3. UIView 创建一个分类,添加 fadeInfadeOut 动画方法。
  4. 创建一个类扩展,为 Person 类添加私有属性和方法。
  5. 比较分类和子类的使用场景,说明在什么情况下应该使用分类而不是子类。

总结

今天我们学习了Objective-C中两个重要的特性:协议和分类。协议定义了一组方法规范,任何类都可以选择实现这些方法,实现了代码的解耦和多态。分类则允许我们为现有类添加新方法,而不需要创建子类或修改原始类。这两种机制都是Objective-C动态特性的体现,极大地增强了代码的灵活性和可扩展性。明天我们将学习Objective-C中的内存管理高级话题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明似水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值