代理设计模式在iOS中的应用

导语

  代理设计模式(也称委托设计模式,以下统称代理模式)是一种非常常见的的设计模式,在程序开发中经常会使用到。文章就代理设计模式在iOS开发中的应用展开叙述。

  为了能形象的理解什么叫代理模式,我们借鉴生活中中介找房的示例来说明。A同学毕业后到一个新的城市找工作,为了能在这个城市安顿下
来而需要租一间房子。但是由于人生地不熟,所以他想通过中介公司寻找到一间合适的房源。事件发展到这里,就引出了我们今天需要讲述的主题——代理设计模式。简而言之,代理就是自己不想做或者不能做的事交付给其他对象去实现,自己只需要接收事情处理的结果(不需要管其他对象的实现过程)的设计模式。

  通常代理模式是同协议配合使用的,这里又引入了协议的概念,协议其实就是声明了一组能被其他类实现,本身却不实现的的方法的集合(也可以说协议是定义公用的一套接口的类),这里如果不太明白,后面讲到具体实例时会详细解释。现在我们已经有了三个对象,这里罗列了每个对象的作用:

  • 协议:用来指定代理双方可以做什么,必须做什么。(类似与合同的概念)
  • 代理:根据指定的协议,完成委托方需要实现的功能。(中介公司)
  • 委托:根据指定的协议,指定代理去完成什么功能。(A同学)

具体实现

下面我们来看看具体的实现:

创建三个文件分别为 Person / Agent / FindHouseProtocol

对应的代码为:

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

@interface Person : NSObject<FindHouseProtocol>

@end
#import "Person.h"

@implementation Person

- (void)findHouse {
    NSLog(@"找到房子啦!");
}

@end
#import <Foundation/Foundation.h>
#import "FindHouseProtocol.h"

@interface Agent : NSObject

@property (nonatomic, weak) id<FindHouseProtocol>delegate;

- (void)startfindHouse;

@end
#import "Agent.h"

@implementation Agent

- (void)startfindHouse {
    
    for (int i = 0; i <100; i ++) {
        if (i == 99) {
            if ([self.delegate respondsToSelector:@selector(findHouse)]) {
                [self.delegate findHouse];
            }
        }
    }
}
@end
#import <Foundation/Foundation.h>

@protocol FindHouseProtocol <NSObject>

@optional
- (void)findHouse;

@end

#import "ViewController.h"
#import "Person.h"
#import "Agent.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [[Person alloc]init];
    Agent *agent = [[Agent alloc]init];
    agent.delegate = person;// 这句话的意思是指定agent的委托人是person对象
    [agent startfindHouse];
    
}
@end

运行,输出结果为:

2018-03-17 02:18:37.959814+0800 DelegateDemo[15177:2140851] 找到房子啦!

原理

  在iOS中代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。

通过上面这张图我们发现,其实委托方的代理属性本质上就是代理对象自身,设置委托代理就是代理属性指针指向代理对象,相当于代理对象只是在委托方中调用自己的方法,如果方法没有实现就会导致崩溃。从崩溃的信息上来看,就可以看出来是代理方没有实现协议中的方法导致的崩溃。

而协议只是一种语法,是声明委托方中的代理属性可以调用协议中声明的方法,而协议中方法的实现还是有代理方完成,而协议方和委托方都不知道代理方有没有完成,也不需要知道怎么完成。

内存管理

为什么我们设置代理属性都使用weak呢?

我们定义的指针默认都是__strong类型的,而属性本质上也是一个成员变量和set、get方法构成的,strong类型的指针会造成强引用,必定会影响一个对象的生命周期,这也就会形成循环引用。

上图中,由于代理对象使用强引用指针,引用创建的委托方LoginVC对象,并且成为LoginVC的代理。这就会导致LoginVC的delegate属性强引用代理对象,导致循环引用的问题,最终两个对象都无法正常释放。

下面两种方式都是弱引用代理对象,但是第一种在代理对象被释放后不会导致崩溃,而第二种会导致崩溃。

@property (nonatomic, weak) iddelegate;
@property (nonatomic, assign) iddelegate;

weak和assign是一种“非拥有关系”的指针,通过这两种修饰符修饰的指针变量,都不会改变被引用对象的引用计数。但是在一个对象被释放后,weak会自动将指针指向nil,而assign则不会。在iOS中,向nil发送消息时不会导致崩溃的,所以assign就会导致野指针的错误unrecognized selector sent to instance。所以我们如果修饰代理属性,还是用weak修饰吧,比较安全。

非正式协议

在iOS2.0之前还没有引入正式协议之前,实现协议的功能主要是通过给NSObject添加Category的方式。这种通过Category的方式,相对于iOS2.0之后引入的正式协议,就叫做非正式协议。

@interface NSObject (CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
- (void)layoutSublayersOfLayer:(CALayer *)layer;
- (nullable id)actionForLayer:(CALayer *)layer forKey:(NSString *)event;
@end
posted @ 2018-03-17 06:08  hwangcheng  阅读(93)  评论(0编辑  收藏  举报