IOS开发OC基础知识(十八) Category、Extension、Block、Protocol、代理

292 阅读10分钟

Category基本概念

1.什么是Category

  • Category有很多种翻译: 分类 \ 类别 \ 类目 (一般叫分类)

  • Category是OC特有的语法, 其他语言没有的语法

  • Category的作用

    • 可以在不修改原来类的基础上, 为这个类扩充一些方法
    • 一个庞大的类可以分模块开发
    • 一个庞大的类可以由多个人来编写,更有利于团队合作

2.Category的格式

  • 在.h文件中声明类别

    • 1)新添加的方法必须写在 @interface 与 @end之间

    • 2)ClassName 现有类的类名(要为哪个类扩展方法) + 3)CategoryName 待声明的类别名称

    • 4)NewMethod 新添加的方法

      @interface ClassName (CategoryName)
      NewMethod; //在类别中添加方法
      //不允许在类别中添加变量
      @end
      
    • 注意: 1)不允许在声明类别的时候定义变量

  • 在.m文件中实现类别:

    • 1)新方法的实现必须写在@ implementation与@end之间
    • 2)ClassName 现有类的类名
    • 3)CategoryName 待声明的类别名称
    • 4)NewMethod 新添加的方法的实现
@implementation ClassName(CategoryName)

NewMethod
... ...
@end
  • 使用Xcode创建分类

转存失败,建议直接上传图片文件

转存失败,建议直接上传图片文件

Category注意事项

1.分类的使用注意事项

  • 分类只能增加方法, 不能增加成员变量
@interface Person (NJ)
{
//    错误写法
//    int _age;
}
- (void)eat;
@end
  • 分类中写property只会生成方法声明
@interface Person (NJ)
// 只会生成getter/setter方法的声明, 不会生成实现和私有成员变量
@property (nonatomic, assign) int age;
@end
  • 分类可以访问原来类中的成员变量
@interface Person : NSObject
{
    int _no;
}
@end

@implementation Person (NJ)
- (void)say
{
    NSLog(@"%s", __func__);
    // 可以访问原有类中得成员变量
    NSLog(@"no = %i", _no);
}
@end
  • 如果分类和原来类出现同名的方法, 优先调用分类中的方法, 原来类中的方法会被忽略
@implementation Person

- (void)sleep
{
    NSLog(@"%s", __func__);
}
@end

@implementation Person (NJ)
- (void)sleep
{
    NSLog(@"%s", __func__);
}
@end

int main(int argc, const char * argv[]) {
    Person *p = [[Person alloc] init];
    [p sleep];
    return 0;
}

输出结果:
-[Person(NJ) sleep]

2.分类的编译的顺序

  • 多个分类中有同名方法,则执行最后编译的文件方法(注意开发中千万不要这么干)
@implementation Person

- (void)sleep
{
    NSLog(@"%s", __func__);
}
@end

@implementation Person (NJ)
- (void)sleep
{
    NSLog(@"%s", __func__);
}
@end

@implementation Person (MJ)
- (void)sleep
{
    NSLog(@"%s", __func__);
}
@end

int main(int argc, const char * argv[]) {
    Person *p = [[Person alloc] init];
    [p sleep];
    return 0;
}

输出结果:
-[Person(MJ) sleep]

转存失败,建议直接上传图片文件

  • 方法调用的优先级(从高到低)

    • 分类(最后参与编译的分类优先)
    • 原来类
    • 父类

类扩展(Class Extension)

1.什么是类扩展

  • 延展类别又称为扩展(Extendsion),Extension是Category的一个特例

  • 可以为某个类扩充一些私有的成员变量和方法

    • 写在.m文件中
    • 英文名是Class Extension

2.类扩展书写格式

@interface 类名 ()
@end
  • 对比分类, 就少了一个分类名称,因此也有人称它为”匿名分类”

Block基本概念

1.什么是Block

  • Block是iOS中一种比较特殊的数据类型

  • Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛

    • 动画
    • 多线程
    • 集合遍历
    • 网络请求回调
  • Block的作用

    • 用来保存某一段代码, 可以在恰当的时间再取出来调用
    • 功能类似于函数和方法

2.block的格式

  • Block的定义格式
返回值类型 (^block变量名)(形参列表) = ^(形参列表) {

};

转存失败,建议直接上传图片文件

  • block最简单形式
void (^block名)() = ^{代码块;}

例如:
void (^myBlock)() = ^{ NSLog(@"李南江"); };
  • block带有参数的block的定义和使用
void (^block名称)(参数列表)
= ^ (参数列表) { // 代码实现; }

例如:
void (^myBlock)(int) = ^(int num){ NSLog(@"num = %i", num); };
  • 带有参数和返回值的block
返回类型 (^block名称)(参数列表)
= ^ (参数列表) { // 代码实现; }

例如:
int (^myBlock)(int, int) = ^(int num1, int num2){ return num1 + num2; };
  • 调用Block保存的代码
block变量名(实参);

typedef和Block

1.函数指针回顾

  • 函数指针使用
int sum(int value1, int value2)
{
    return value1 + value2;
}

int minus(int value1, int value2)
{
    return value1 - value2;
}

int main(int argc, const char * argv[]) {
    int (*sumP) (int, int) = sum;
    int res = sumP(10, 20);
    NSLog(@"res = %i", res);

    int (*minusP) (int , int) = minus;
    res = minusP(10, 20);
    NSLog(@"res = %i", res);
    return 0;
}
  • 函数指针别名
typedef int (*calculate) (int, int);
int main(int argc, const char * argv[]) {
    calculate sumP = sum;
    int res = sumP(10, 20);
    NSLog(@"res = %i", res);
    calculate minusP = minus;
    res = minusP(10, 20);
    NSLog(@"res = %i", res);
    return 0;
}

2.block和typedef

  • block使用

    int main(int argc, const char * argv[]) {
      int (^sumBlock) (int, int) = ^(int value1, int value2){
          return value1 + value2;
      };
      int res = sumBlock(10 , 20);
      NSLog(@"res = %i", res);
    
      int (^minusBlock) (int, int) = ^(int value1, int value2){
          return value1 - value2;
      };
      res = minusBlock(10 , 20);
      NSLog(@"res = %i", res);
      return 0;
    }
    
  • block别名

int main(int argc, const char * argv[]) {
    calculateBlock sumBlock = ^(int value1, int value2){
        return value1 + value2;
    };
    int res = sumBlock(10, 20);
    NSLog(@"res = %i", res);
    calculateBlock minusBlock = ^(int value1, int value2){
        return value1 - value2;
    };
    res = minusBlock(10, 20);
    NSLog(@"res = %i", res);

    return 0;
}

Block注意事项

1.Block注意事项

  • 在block内部可以访问block外部的变量
int  a = 10;
void (^myBlock)() = ^{
    NSLog(@"a = %i", a);
    }
myBlock();
输出结果: 10
  • block内部也可以定义和block外部的同名的变量(局部变量),此时局部变量会暂时屏蔽外部
int  a = 10;
void (^myBlock)() = ^{
    int a = 50;
    NSLog(@"a = %i", a);
    }
myBlock();
输出结果: 50
  • 默认情况下, Block内部不能修改外面的局部变量
int b = 5;
void (^myBlock)() = ^{
    b = 20; // 报错
    NSLog(@"b = %i", b);
    };
myBlock();
  • Block内部可以修改使用__block修饰的局部变量

    __block int b = 5;
    void (^myBlock)() = ^{
      b = 20;
      NSLog(@"b = %i", b);
      };
    myBlock();
    输出结果: 20
    

Protocol基本概念

1.protocol 基本概念

  • Protocol翻译过来, 叫做”协议”

    • 在写java的时候都会有接口interface这个概念,接口就是一堆方法的声明没有实现,而在OC里面Interface是一个类的头文件的声明,并不是真正意义上的接口的意思,在OC中接口是由一个叫做协议的protocol来实现的
    • protocol它可以声明一些必须实现的方法和选择实现 的方法。这个和java是完全不同的
  • Protocol的作用

    • 用来声明一些方法
    • 也就说, 一个Protocol是由一系列的方法声明组成的

2.protocol 语法格式

  • Protocol的定义
@protocol 协议名称
// 方法声明列表
@end
  • 类遵守协议

    • 一个类可以遵守1个或多个协议
    • 任何类只要遵守了Protocol,就相当于拥有了Protocol的所有方法声明
@interface 类名 : 父类 <协议名称1, 协议名称2,…>
@end
  • 示例
@protocol SportProtocol <NSObject>
- (void)playFootball;
- (void)playBasketball;
@end

#import "SportProtocol.h" // 导入协议
@interface Studnet : NSObject<SportProtocol> // 遵守协议
@end

@implementation Student
// 实现协议方法
- (void)playBasketball
{
    NSLog(@"%s", __func__);
}
// 实现协议方法
- (void)playFootball
{
    NSLog(@"%s", __func__);
}
@end

3.protocol和继承区别

  • 继承之后默认就有实现, 而protocol只有声明没有实现
  • 相同类型的类可以使用继承, 但是不同类型的类只能使用protocol
  • protocol可以用于存储方法的声明, 可以将多个类中共同的方法抽取出来, 以后让这些类遵守协议即可

Protocol其他用法

1.protocol 的使用注意

  • 1)Protocol:就一个用途,用来声明一大堆的方法(不能声明成员变量),不能写实现。
@protocol SportProtocol <NSObject>
{
    int _age; // 错误写法
}
- (void)playFootball;
- (void)playBasketball;
@end
  • 2)只要父类遵守了某个协议,那么子类也遵守。
@protocol SportProtocol <NSObject>

- (void)playFootball;
- (void)playBasketball;
@end
#import "SportProtocol.h"
@interface Student : NSObject <SportProtocol>
@end

@interface GoodStudent : Student
@end

@implementation GoodStudent
- (void)playFootball
{
    NSLog(@"%s", __func__);
}
- (void)playBasketball
{
    NSLog(@"%s", __func__);
}
@end
  • 3)OC不能继承多个类(单继承)但是能够遵守多个协议。继承(:),遵守协议(< >)
#import "SportProtocol.h"
#import "StudyProtocol.h"

@interface Student : NSObject <SportProtocol, StudyProtocol>

@end
  • 4)协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明
@protocol A
-(void)methodA;
@end

@protocol B <A>
-(void)methodB;
@end
@interface Student : NSObject <B>
-(void)methodA; // 同时拥有A/B协议中的方法声明
-(void)methodB;
@end

2.基协议

  • NSObject是一个基类,最根本最基本的类,任何其他类最终都要继承它

  • 还有名字也叫NSObject的协议,它是一个基协议,最根本最基本的协议

  • NSObject协议中声明很多最基本的方法

    • description
    • retain
    • release
  • 建议每个新的协议都要遵守NSObject协议

@protocol SportProtocol <NSObject> // 基协议

- (void)playFootball;
- (void)playBasketball;
@end

3.@required和@optional关键字

  • 协议中有2个关键字可以控制方法是否要实现(默认是@required,在大多数情况下,用途在于程序员之间的交流)

    • @required:这个方法必须要实现(若不实现,编译器会发出警告)
    • @optional:这个方法不一定要实现
@protocol SportProtocol <NSObject>

@required // 如果遵守协议的类不实现会报警告
- (void)playFootball;
@optional // 如果遵守协议的类不实现不会报警告
- (void)playBasketball;
@end

Protocol类型限制

1.protocol类型限制

  • 设定情景:

    • 某攻城狮A希望找一个会做饭、洗衣服的女生做女朋友,有国企工作的优先。
    • 满足条件的女生都可以向他发送消息
  • 从题目中我们得到要求

    • 会做饭
    • 会洗衣服
    • 有份好工作
@protocol WifeCondition<NSObject>
- (void)cooking;
- (void)washing;
- (void)job;
@end
  • 如何在代码中要求对象必须具备这些行为?

    • 数据类型<协议名称> 变量名
// 如果没有遵守协议则会报警告
id<WifeCondition> wife = [[Person alloc] init];

代理设计模式

1.

  • 什么是设计模式

    • 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
  • 什么是代理设计模式

    • 生活中大家一定遇到这样的情况了:比如说我要买一包纸,不妨就是心相印的吧,那一般人的话我应该不是去心相印的工厂里面直接去买吧,而是我们在心相印专卖店或者什么超市啊,这些地方购买,这些地方实际上就是洁丽雅毛巾的代理。这其实和我们OO中的代理模式是很相似的。
  • 代理设计模式的场合:

    • 当对象A发生了一些行为,想告知对象B(让对象B成为对象A的代理对象)
    • 对象B想监听对象A的一些行为(让对象B成为对象A的代理对象)
    • 当对象A无法处理某些行为的时候,想让对象B帮忙处理(让对象B成为对象A的代理对象)

2.代理设计模式示例

  • 婴儿吃饭睡觉
// 协议
#import <Foundation/Foundation.h>
@class Baby;

@protocol BabyProtocol <NSObject>
- (void)feedWithBaby:(Baby *)baby;
- (void)hypnosisWithBaby:(Baby *)baby;
@end
#import "BabyProtocol.h"
@interface Baby : NSObject
// 食量
@property (nonatomic, assign) int food;
// 睡意
@property (nonatomic, assign) int drowsiness;
// 饿
- (void)hungry;
// 睡意
- (void)sleepy;
@property (nonatomic, strong) id<BabyProtocol> nanny;
@end

@implementation Baby

- (void)hungry
{
    self.food -= 5;
    NSLog(@"婴儿饿了");
    // 通知保姆
    if ([self.nanny respondsToSelector:@selector(feedWithBaby:)]) {
        [self.nanny feedWithBaby:self];
    }
}

- (void)sleepy
{
    self.drowsiness += 5;
    NSLog(@"婴儿困了");
    // 通知保姆
    if ([self.nanny respondsToSelector:@selector(hypnosisWithBaby:)]) {
        [self.nanny hypnosisWithBaby:self];
    }
}
@end
// 保姆
@interface Nanny : NSObject <BabyProtocol>
@end

@implementation Nanny

- (void)feedWithBaby:(Baby *)baby
{
    baby.food += 10;
    NSLog(@"给婴儿喂奶, 现在的食量是%i", baby.food);
}

- (void)hypnosisWithBaby:(Baby *)baby
{
    baby.drowsiness += 10;
    NSLog(@"哄婴儿睡觉, 现在的睡意是%i", baby.drowsiness);
}
@end
  • 有一个婴儿,他本身不会自己吃饭和洗澡等等一些事情,于是婴儿就请了一个保姆,于是婴儿和保姆之间商定了一个协议,协议中写明了保姆需要做什么事情,而保姆就是这个代理人,即:婴儿和保姆之间有个协议,保姆遵守该协议,于是保姆就需要实现该协议中的条款成为代理人

3.代理设计模式练习

  • 学生通过中介找房子的过程,学生不知道怎么找所以让代理帮忙找