适配器模式初探

Author Avatar
XibHe 2月 19, 2017
  • 在其它设备中阅读本文章

初衷

解决ViewConctroller里冗余代码过多的问题,将公用模块抽离出来。这里将项目中用到的拍照,调用相册,录像,定位等功能单独抽离出来放在一起。

使用步骤

  • 第一步: 定义一个delegate,用来返回一个遵守该协议的数据,不管传过来的数据是什么类型,只要服从这个协议,实现协议里的方法,就处理这个数据。
@protocol MediaHandleDelegate <NSObject>
@optional
- (void)returnMedidaData:(id)mediaData;
@end
  • 第二步: 创建继承于NSObject的根适配器类MediaHandleAct,该类遵守第一步中的协议,并实现该协议的方法,但只是空实现。
// MediaHandleAct.h

#import "MediaHandleDelegate.h"

@interface MediaHandleAct : NSObject
{
    id <MediaHandleDelegate> _delegate;
}
// MediaHandleAct.m

#import "MediaHandleAct.h"

- (void)returnMedidaData:(NSString *)data{
}
  • 第三步: 构建根适配器类MediaHandleAct中注册特定Action的方法,并获取注册的Action。
#import <Foundation/Foundation.h>
#import "MediaHandleDelegate.h"

@interface MediaHandleAct : NSObject
{
    id <MediaHandleDelegate> _delegate;
}
// 动态注册新的Action
+ (void)registerMediaHandleAction:(MediaHandleAct *)mediaHandleAct andKey:(NSString *) actKey;
// 获取注册的Action
+ (NSDictionary *)mediaHandleActions;
// 传递Action的具体执行方法
- (void)performActionWichController:(UIViewController *)controller callbackClass:(NSObject *)callbackClass;
@end
// MediaHandleAct.m
#import "MediaHandleAct.h"
static NSMutableDictionary * mediaHandleActs = nil;

@implementation MediaHandleAct

+ (void)registerMediaHandleAction:(MediaHandleAct *)mediaHandleAct andKey:(NSString *)actKey
{
    @synchronized(self){
        if (!mediaHandleActs)
        {
            mediaHandleActs = [NSMutableDictionary dictionary];
            [MediaHandleAct registerCommonAction];
        }
        [mediaHandleActs setObject:mediaHandleAct forKey:actKey];
    }
}

+ (NSDictionary *)mediaHandleActions
{
    NSDictionary * acts = nil;
    @synchronized(self){
        if (!mediaHandleActs)
        {
            mediaHandleActs = [NSMutableDictionary dictionary];
            [MediaHandleAct registerCommonAction];
        }
        acts = mediaHandleActs;
    }

    return acts;
}
// 具体的执行方法
- (void)performActionWichController:(UIViewController *)controller callbackClass:(NSObject *)callbackClass{
}
@end

synchronized 这个主要是考虑多线程的程序,这个指令可以将{ } 内的代码限制在一个线程执行,如果某个线程没有执行完,其他的线程如果需要执行就得等着。@synchronized 的作用是创建一个互斥锁,保证此时没有其它线程对self对象进行修改。这种机制确保了同一时刻对于每一个类,至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突。一般在公用变量的时候使用,如单例模式或者操作类的static变量中使用。

  • 第四步: 针对不同功能的类,创建继承于MediaHandleAct的类适配器。
    使用适配器的时候,分为类适配器和对象适配器。类适配器针对每一个数据类型或某个功能模块创建一个适配器,并继承于根适配器。
// 调用相册功能
#import <Foundation/Foundation.h>
#import "MediaHandleAct.h"

@interface MediaHandleAlbum : MediaHandleAct
@property (nonatomic, assign) NSInteger maxCount;   // 最多可添加照片数
// 注册Action
+ (void)load;
@end
// MediaHandleAlbum.m
#import "MediaHandleAlbum.h"
#import "JKImagePickerController.h"
@interface MediaHandleAlbum ()<JKImagePickerControllerDelegate>
{
    UIViewController * _parentController;
}
@end

@implementation MediaHandleAlbum

+ (void)load
{
    [MediaHandleAct registerMediaHandleAction:[[MediaHandleAlbum alloc] init] andKey:@"album"];
}

#pragma mark - 拍照和调用相册功能
- (void)performActionWichController:(UIViewController *)controller callbackClass:(NSObject *)callbackClass
{
    _parentController = controller;
    _delegate = (id)callbackClass;

    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:(id)self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:@"拍照",@"从相册选择",@"取消",nil];
    [actionSheet showInView:_parentController.view];
}

#pragma mark - ActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    switch (buttonIndex) {
        case 0:
        {
            // 拍照
            UIImagePickerController *picker = [[UIImagePickerController alloc] init];
            picker.sourceType = UIImagePickerControllerSourceTypeCamera;
            picker.editing = YES;
            picker.allowsEditing = YES;
            picker.delegate = (id)self;
            [_parentController presentViewController:picker animated:YES completion:nil];

            break;
        }
        case 1:
        {
            // 调用相册
            JKImagePickerController *imagePickerController = [[JKImagePickerController alloc] init];
            imagePickerController.delegate = (id)self;
            imagePickerController.showsCancelButton = YES;
            imagePickerController.allowsMultipleSelection = YES;
            imagePickerController.minimumNumberOfSelection = 1;
            imagePickerController.maximumNumberOfSelection = self.maxCount;
            //imagePickerController.selectedAssetArray = self.photosView.assetsArray;
            UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imagePickerController];
            [_parentController presentViewController:navigationController animated:YES completion:NULL];

            break;
        }
        default:
            break;
    }
}

#pragma mark - UIImagePickerControllerDelegate
- (void) imagePickerController: (UIImagePickerController*)picker didFinishPickingMediaWithInfo: (NSDictionary*) info
{
    [picker dismissViewControllerAnimated:YES completion:nil];

    UIImage *edit = [info objectForKey:@"UIImagePickerControllerEditedImage"];
    UIImage *originalImage = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
    CLog(@" EditedImage %@ ",NSStringFromCGSize(edit.size));
    CLog(@" OriginalImage %@ ",NSStringFromCGSize(originalImage.size));
    UIImage *aImage = edit;
    NSData * imageData = UIImageJPEGRepresentation(aImage,0.5);

    if (_delegate && [_delegate respondsToSelector:@selector(returnMedidaData:)])
    {
        NSArray * photos = @[imageData];
        [_delegate returnMedidaData:photos];
    }
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [picker dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - JKImagePickerControllerDelegate
- (void)imagePickerController:(JKImagePickerController *)imagePicker didSelectAsset:(JKAssets *)asset isSource:(BOOL)source
{
    [imagePicker dismissViewControllerAnimated:YES completion:nil];
}

- (void)imagePickerController:(JKImagePickerController *)imagePicker didSelectAssets:(NSArray *)assets isSource:(BOOL)source
{
    [imagePicker dismissViewControllerAnimated:YES completion:nil];

    CLog(@"assets == %@",assets);
    NSMutableArray * photos = [[NSMutableArray alloc] init];

    for (JKAssets * jkAssets in assets)
    {
        NSData * photoData = UIImageJPEGRepresentation(jkAssets.photo, 0.5);
        [photos addObject:photoData];
        CLog(@"groupPropertyID = %@, groupPropertyURL = %@, assetPropertyURL = %@, photo = %@",jkAssets.groupPropertyID,jkAssets.groupPropertyURL,jkAssets.assetPropertyURL,jkAssets.photo);
    }

    if (_delegate && [_delegate respondsToSelector:@selector(returnMedidaData:)])
    {
        [_delegate returnMedidaData:photos];
    }

}
  • 第五步: 调用适配器。
    MediaHandleAlbum * album = [[MediaHandleAct mediaHandleActions] objectForKey:@"album"];
    album.maxCount = imageCount;
    [album performActionWichController:self callbackClass:self];

适配器模式的优缺点

优点: 耦合度低,可以扩展多种不同功能的适配样式。
缺点: 代码量相应增多,文件增多。

源码下载地址

注意

该Demo中使用调用相机,相册,录制视频的功能,当系统为iOS 10以上访问权限的设置有所不同,需要在工程中的plist文件中添加对应的描述。Demo中的plist,
如图,
工程截图

其他功能访问权限设置,
iOS 10 - Changes in asking permissions of Camera, microphone and Photo Library causing application to crash

–EOF–

若无特别说明,本站文章均为原创,转载请保留链接,谢谢