天天看點

Objective-C 程式設計之道 iOS設計模式解析--第22章 代理

第22章 代理

22.1 何為代理模式

遠端代理(remote proxy)為位于不同位址空間或網絡上的對象提供本地代表。

虛拟代理(virtual proxy)根據需要建立重型對象。

保護代理(protection proxy)根據各種通路權限控制對原對象的通路。

智能引用代理(smart-reference proxy)通過對真正對象的引用進行計數管理記憶體。也用于鎖定真正對象,讓其他對象不能對其進行修改。

代理模式:為其他對象提供一種代理一控制對這個對象的通路。

通常,代理是一種代替活着占位,她控制對另一個對象的通路,而這些對象可能是遠端對象,建立的開銷較大的對象,或者是對安全性有要求的對象。這裡沒有辦法對這些代理作一一介紹。

在運作時,我們可以想象這樣一個場景:用戶端一抽象類型引用一個對象。這個引用實際上是個Proxy對象。Proxy對象本身有一個對RealSubject執行個體的引用,以後如果接到請求,此執行個體将執行高強度的工作。

22.2 何時使用代理模式

22.3 用虛拟代理懶加載圖像

塗鴉縮略圖視圖的設計與實作

//———————————————

@interface ScribbleThumbnailView : UIView

{

    @protected

    NSString *imagePath_;

}

@property (nonatomic,readonly) UIImage *image;

@property (nonatomic,copy) NSString *imagePath;

@end

#import “ScribbleThumbnailView.h”

#implementation ScribbleThumbnailView

@dynamic image;

@synthesize imagePath= imagePath_;

@end

//———————————————

#import “ScribbleThumbnailView.h”

@interface ScribbleThumbnailViewImageProxy : ScribbleThumbnailView

{

    @private

    UIImage *realImage_;

    BOOL loadingThreadHasLaunched_;

}

@property (nonatomic, readonly) UIImage *image;

@end;

#import “ScribbleThumbnailViewImageProxy.h”

@interface ScribbleThumbnailViewImageProxy()

-(void) forwardImageLoadingThread;

@end

@implementation ScribbleThumbnailViewImageProxy

@dynamic imagePath;

- (UIImage*)image

{

    if (realImage_ == nil)

    {

        realImage_ = [[UIImage alloc] initWithContentOfFile:imagePath_];

    }

    return realImage_;

}

- (void)drawRect:(CGRect)rect

{

    if (realImage_ == nil)

    {

        //繪制占位圖像

    }

    if(!loadingThreadHasLaunched_)

    {

        [self performSelectorInBackground:@select(forwardImageLoadingThread) withObject:nil];

        loadingThreadHasLaunched_ = YES;

    }

    else

    {

        [realImage_ drawInRect:rect];

    }

}

- (void)dealloc

{

    [realImage_ realease];

    [super dealloc];

}

-(void) forwardImageLoadingThread

{

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [self image];

    [self performSelectorInMainThread:@selector(setNeedsDispaly) withObject:nil];

    [pool release];

}

@end

//———————————————

22.4 在Cocoa Touch架構中使用代理模式

//—————————————————————————

MyProtocol.h

//

//  MyProtocol.h

//  NSProxyTest

//

//  Created by ranzhou on 16/7/6.

//  Copyright © 2016年 ranzhouee. All rights reserved.

//

#import <Foundation/Foundation.h>

@protocol MyProtocol <NSObject>

@optional

-(void)loadImage;

@required

@end

//—————————————————————————

//

//  RealObject.h

//  NSProxyTest

//

//  Created by ranzhou on 16/7/6.

//  Copyright © 2016年 ranzhouee. All rights reserved.

//

#import <Foundation/Foundation.h>

#import "MyProtocol.h"

@interface RealObject : NSObject<MyProtocol>

@end

//—————————————————————————

//

//  RealObject.m

//  NSProxyTest

//

//  Created by ranzhou on 16/7/6.

//  Copyright © 2016年 ranzhouee. All rights reserved.

//

#import "RealObject.h"

@implementation RealObject

-(void)loadImage

{

    static dispatch_once_t once;

    dispatch_once(&once, ^{

        [NSThread sleepForTimeInterval:5.0];

        // 第一次加載圖像,需要5秒鐘。

    });

    NSLog(@"%@%@",NSStringFromClass([self class]),NSStringFromSelector(_cmd));

}

@end

//—————————————————————————

//

//  TestProxy.h

//  NSProxyTest

//

//  Created by ranzhou on 16/7/6.

//  Copyright © 2016年 ranzhouee. All rights reserved.

//

#import <Foundation/Foundation.h>

#import "MyProtocol.h"

@interface TestProxy : NSProxy<MyProtocol>

//TestProxy中需要實作初始化方法,否則 self.testProxy = [[TestProxy alloc]init];或報編譯錯誤//No visible @interface for 'TestProxy' declares the selector 'init';

//- (instancetype)init;//個人覺得不init也無所謂。

@end

//—————————————————————————

//

//  TestProxy.m

//  NSProxyTest

//

//  Created by ranzhou on 16/7/6.

//  Copyright © 2016年 ranzhouee. All rights reserved.

//

#import "TestProxy.h"

#import "RealObject.h"

@interface TestProxy ()

@property (nonatomic,strong) RealObject *realObject;

@end

@implementation TestProxy

- (RealObject*)toInitRealObject {

    static dispatch_once_t once;

    dispatch_once(&once, ^{

        if (!self.realObject) {

            self.realObject = [[RealObject alloc]init];

        }

    });

    return self.realObject;

}

- (void)loadImage {

    if (!self.realObject)

    {

        NSLog(@"随便做點事情,等self.realObject初始化完成再處理");

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            [self toInitRealObject];

            [self.realObject loadImage];

        });

    }

    else

    {

        [self.realObject loadImage];

    }

}

- (void)forwardInvocation:(NSInvocation *)invocation

{

    if (self.realObject)

    {

        [invocation setTarget:self.realObject];

        [invocation invoke];

    }

    else

    {

        NSLog(@"%@%@",@"self.realObject is nil",NSStringFromSelector(_cmd));

    }

}

- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel

{

    if (self.realObject) {

        return [self.realObject methodSignatureForSelector:sel];

    }

    else

    {

        NSLog(@"%@%@",@"self.realObject is nil",NSStringFromSelector(_cmd));

        return [super methodSignatureForSelector:sel];

    }

}

@end

//—————————————————————————

測試代碼:

    self.testProxy = [TestProxy alloc];

    [self.testProxy loadImage];

    [self.testProxy loadImage];

}

2016-07-06 21:15:26.276 NSProxyTest[1684:298600] 随便做點事情,等self.realObject初始化完成再處理

2016-07-06 21:15:26.277 NSProxyTest[1684:298600] 随便做點事情,等self.realObject初始化完成再處理

2016-07-06 21:15:31.279 NSProxyTest[1684:298671] RealObjectloadImage

2016-07-06 21:15:31.279 NSProxyTest[1684:298663] RealObjectloadImage

//—————————————————————————