代码改变世界

iOS,几种设计模式

2016-09-22 15:00  帅不过三秒  阅读(234)  评论(0编辑  收藏  举报

1.单例模式

2.观察者模式

3.委托代理

4.block回调

5.反射机制

6.多代理模式

单例模式

iOS单例模式的2种方式。根据线程安全的实现来区分,一种是使用@synchronized ,另一种是使用GCD的dispatch_once函数。

要实现单例,首先需要一个static的指向类本身的对象,其次需要一个初始化类函数。下面是两种实现的代码。

@synchronized

static InstanceClass *instance;
+ (InstanceClass *)shareInstance{
    @synchronized (self){
        if (instance == nil) {
            instance = [[InstanceClass alloc] init];
        }
    }
    return instance;
}

 

GCD

static InstanceClass *instance;
+ (InstanceClass *)shareInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[InstanceClass alloc] init];
    });
    return instance;
}

 

示例:

+(MyClass *)shareInstance
{
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [[self alloc]init];
    });
    return instance;
}

 

 

观察者模式

 

Notification(通知)

 

//向通知中心添加消息监听名称,和通知方法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDisappear:) name:@"DismissFaceVerifyController" object:nil];

//通过通知中心调用对应消息名
[[NSNotificationCenter defaultCenter] postNotificationName:@"DismissFaceVerifyController" object:self userInfo:nil];

 

KVO(键值监听),即Key-Value Observing

监听对象的属性(成员变量)变化,对象可以是自己也可以是其它

//实例监听自己的属性

//ViewController.m文件

//
//  ViewController.m
//  KVOLearn
//
//  Created by Vie on 2017/4/6.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
@property(strong, nonatomic) NSString *testStr;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    self.testStr=@"Tracer";
    
    //按钮
    UIButton *loginBtn=[[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-50, 350, 100, 50)];
    [loginBtn setTitle:@"改变值" forState:UIControlStateNormal];
    [loginBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [loginBtn setBackgroundColor:[UIColor redColor]];
    [loginBtn.layer setBorderWidth:0.2f];
    [loginBtn.layer setCornerRadius:10.0f];
    [loginBtn addTarget:self action:@selector(changeAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:loginBtn];
    
    /*1.注册对象self为被观察者: option中,
     forKeyPath为被观察的对象属性
     NSKeyValueObservingOptionOld 以字典的形式提供 “初始对象数据”;
     NSKeyValueObservingOptionNew 以字典的形式提供 “更新后新的数据”; */
    [self addObserver:self forKeyPath:@"testStr" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
//按钮事件
-(void)changeAction:(UIButton *)sender{
    self.testStr=@"Vie";
}

/* 只要object的keyPath属性发生变化,就会调用此回调方法*/
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"testStr"]) {
        NSLog(@"监听到testStr属性值改变");
    }
}
-(void)dealloc{
    //移除self中forKeyPath属性观察
    [self removeObserver:self forKeyPath:@"testStr" context:nil];
}


@end

 

 

 

委托代理

delegate

委托应用场景:一个view把一些业务交给 控制器去实现,然后控制器实现委托改变view 

//在ViewController.h中声明协议如下 

@protocol ByValueDelegate <NSObject> 
-(void) passValue:(NSString *) value;
@end 

 

//在ViewController.h中声明委托变量 

@property (nonatomic,weak) id<ByValueDelegate> delegate;//声明一个委托变量 

 

//在ViewController.m中设置代理 

UserinfoView *uiv=[[UserinfoView alloc]init]; 
//把值委托到UserinfoView.m的实现委托的方法里面 
self.delegate=uiv;//设置该对象代理为对象uiv 
[self.delegate passValue:@"委托传值跳转"]; 

 





//在UserinfoView.h中,引用ViewController的头文件,并添加代理协议如下 

#import "ViewController.h" 

@interface UserinfoView : UIViewController<ByValueDelegate>@property (nonatomic,strong) UILabel *showLable; 
@property (nonatomic,retain) UserinfoEntity *showUserinfoEntity; 
-(void) viewDidLoad;@end 


//在UserinfoView.m中实现代理函数 

//实现委托方法 
-(void) passValue:(NSString *)value{
   UIAlertView *alterOk = [[UIAlertView alloc] initWithTitle:@"提示" message:value delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; 
   [alterOk show]; 
}

 

KVC(键值编码),即Key-Value Coding,一个非正式的Protocol,使用字符串(键)访问一个对象实例变量的机制

就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。

KVC在iOS中的定义

无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-c中有个显式的NSKeyValueCoding类别名,而Swift没有,也不需要)所以对于所有继承了NSObject在类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法
//KVC最为重要的四个方法

- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

 //例

//PersonInfoModel.h文件

//
//  PersonInfoModel.h
//  KVCLearn
//  个人信息
//  Created by Vie on 2017/4/6.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface PersonInfoModel : NSObject
@property(strong, nonatomic) NSString *name;//姓名
@property(strong, nonatomic) NSString *gender;//性别
@property(assign, nonatomic) NSNumber *age;//年龄
@end

 

//StudentInfoModel.h文件

//
//  StudentInfoModel.h
//  KVCLearn
//  学生信息
//  Created by Vie on 2017/4/6.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "PersonInfoModel.h"
@interface StudentInfoModel : NSObject
@property(strong, nonatomic)PersonInfoModel *personModel;//个人信息
@property(strong, nonatomic)NSString *studentID;//学号
@property(strong, nonatomic)NSString *departments;//院系
@end

 

//使用

#import "StudentInfoModel.h"



 StudentInfoModel *studentModel=[[StudentInfoModel alloc] init];
 //设值
 [studentModel setValue:@"信息技术学院" forKey:@"departments"];
 [studentModel setValue:@"Vie" forKeyPath:@"personModel.name"];
 //取值
 NSLog(@"%@",[studentModel valueForKey:@"departments"]);
 NSLog(@"%@",[studentModel valueForKeyPath:@"departments"]);

 

修改系统控件内部属性(runtime + KVC)

将UIPageControl样式原来样式

修改为下面

 

 

PageControl公开属性没有直接可以修改的,目前可以自定义PageControl,这种方式看起来不简单。另一种方式就是,通过runtime遍历出UIPageControl所有属性(包括私有成员属性,runtime确实很强大)。

 //遍历属性
      unsigned int count = 0;
      Ivar *ivars = class_copyIvarList([UIPageControl class], &count);
      for (int i = 0; i < count; i++) {
          Ivar ivar = ivars[i];
          //获取所有私有属性
          const char *property = ivar_getName(ivar);
          NSLog(@"%@",[[NSString alloc]initWithCString:property encoding:NSUTF8StringEncoding]);
      }

//运行结果

2017-04-06 20:12:20.756 KVCLearn[4430:220644] _lastUserInterfaceIdiom
2017-04-06 20:12:20.756 KVCLearn[4430:220644] _indicators
2017-04-06 20:12:20.757 KVCLearn[4430:220644] _currentPage
2017-04-06 20:12:20.757 KVCLearn[4430:220644] _displayedPage
2017-04-06 20:12:20.757 KVCLearn[4430:220644] _pageControlFlags
2017-04-06 20:12:20.757 KVCLearn[4430:220644] _currentPageImage
2017-04-06 20:12:20.757 KVCLearn[4430:220644] _pageImage
2017-04-06 20:12:20.757 KVCLearn[4430:220644] _currentPageImages
2017-04-06 20:12:20.758 KVCLearn[4430:220644] _pageImages
2017-04-06 20:12:20.758 KVCLearn[4430:220644] _backgroundVisualEffectView
2017-04-06 20:12:20.758 KVCLearn[4430:220644]_currentPageIndicatorTintColor
2017-04-06 20:12:20.758 KVCLearn[4430:220644] _pageIndicatorTintColor
2017-04-06 20:12:20.758 KVCLearn[4430:220644] _legibilitySettings
2017-04-06 20:12:20.758 KVCLearn[4430:220644] _numberOfPages

//找到了属性后,接着实现

//然后通过KVC设置自定义图片,实现了效果,代码如下:
 UIPageControl *pageControl = [[UIPageControl alloc] init]; 
 [pageControl setValue:[UIImage imageNamed:@"home_slipt_nor"] forKeyPath:@"_pageImage"];
 [pageControl setValue:[UIImage imageNamed:@"home_slipt_pre"] forKeyPath:@"_currentPageImage"];

 

block回调

    

     block中使用修改方法外的属性需要用__block修饰,例如:

__block BOOL whoopsSomethingWrongHappened = true;

//ViewController.m文件
#import "ViewController.h"
#import "CallbackTest.h"
@interface ViewController ()

@end
 
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view setBackgroundColor:[UIColor whiteColor]];
    UIButton *backBtn=[[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width*0.4, self.view.frame.size.height*0.475, self.view.frame.size.width*0.2, self.view.frame.size.height*0.05)];
    [backBtn.layer setCornerRadius:5.0];
    [backBtn setTitle:@"回调" forState:UIControlStateNormal];
    [backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [backBtn.layer setBorderWidth:0.5];
    [backBtn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:backBtn];
}

/**
 *  @Author Vie, 2015-08-05 13:41:37
 *  使用block回调函数
 *  @param sender sender description
 *
 *  @since <#version number#>
 */
-(void)btnAction:(UIButton *)sender{
    CallbackTest *backTest=[[CallbackTest alloc] init];
    //第一种回调方式
//    [backTest startString:@"回调开始" callBack:^(NSString *backFunstring) {
//     回调时候会执行
//        UIAlertView  *alert=[[UIAlertView alloc] initWithTitle:@"回调提示" message:backFunstring delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
//        [alert show];
//
//    }];

   
    //第二种回调方式
    backTest.backCall=^(NSString *string){
        UIAlertView  *alert=[[UIAlertView alloc] initWithTitle:@"回调提示"message:string delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
        [alert show];
    };
    [backTest blockCall:@"回调开始"];
}
@end

 

//CallbackTest.h文件
#import <UIKit/UIKit.h>
@interface CallbackTest : NSObject
//定义一个block,返回值为void,参数为NSString
typedef void(^backFunction)(NSString *backFunstring);
@property (nonatomic,copy) backFunction backCall;
-(void)startString:(NSString *)str callBack:(backFunction)backString;
-(void)blockCall:(NSString *)str;//第一一个block属性
@end

 

//CallbackTest.m文件
#import "CallbackTest.h"
 
@implementation CallbackTest
//第一种回调方式
-(void)startString:(NSString *)str callBack:(backFunction)backString{
    NSMutableString *mutString=[str mutableCopy];
    [mutString appendString:@"回调完成"];
    //执行block
//    backString(mtuString);
     //将block回调交给另一个方法执行
    [self TestBackCall:mutString callBack:backString];
}

//执行block回调的函数
-(void)TestBackCall:(NSMutableString *)mutString callBack:(backFunction)testBack{
    testBack(mutString);
}

//第二种回调方式
-(void)blockCall:(NSString *)str{
    NSMutableString *mutString=[str mutableCopy];
    [mutString appendString:@"回调完成"];
    _backCall(mutString);
}
@end

 

反射机制

//使用NSClassFromString,通过字符串获取类 
Class class=NSClassFromString(@"UIAlertView");
//或者使用objc_getClass,通过字符串获取类 
//Class class=objc_getClass("UIAlertView");
id  alert=  [[class alloc] initWithTitle:@"123" message:@"111" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];

 

多代理模式

  使用NSPointerArray进行多代理可以跟踪内存,它是mutable,数组不会增加这个对象的引用计数

//例

//MoreDelegate.h文件

//
//  MoreDelegate.h
//  MoreDelegate
//  委托
//  Created by Vie on 2017/5/2.
//  Copyright © 2017年 Vie. All rights reserved.
//
@protocol MoreDelegate <NSObject>

@optional
-(void)testMoreDelegate;//多代理测试

@end

//后面可以结合通知(或者其他方式)使用,到达方法准确调用带参。

//MoreDelegateManager.h文件

//
//  MoreDelegateManager.h
//  MoreDelegate
//  多代理管理实现类
//  Created by Vie on 2017/5/2.
//  Copyright © 2017年 Vie. All rights reserved.
//

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

@interface MoreDelegateManager : NSObject


/**
 单例模式
 
 @return self
 */
+(MoreDelegateManager *)shareInstance;

/**
 注册代理
 
 @param obj 需要实现代理的对象
 */
-(void)registerDelegate:(id)obj;


/**
 解除代理
 
 @param obj 需要接触代理的对象
 */
-(void)unRegisterDelegate:(id)obj;


/**
 执行代理方法(后续可以多个方法提供出去,方便传参调用)

 @param aSelector 类方法选择器
 */
-(void)delegateAction:(SEL)aSelector;
@end

 

//MoreDelegateManager.m文件

//
//  MoreDelegateManager.m
//  MoreDelegate
//
//  Created by Vie on 2017/5/2.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "MoreDelegateManager.h"


#import <objc/runtime.h>
#import <objc/message.h>

@interface MoreDelegateManager ()<MoreDelegate>
@property(nonatomic,strong) NSPointerArray *delegateArr;//实现委托的数组
@end

@implementation MoreDelegateManager
#pragma mark 懒加载
-(NSPointerArray *)delegateArr{
    if (!_delegateArr) {
        _delegateArr=[NSPointerArray weakObjectsPointerArray];
    }
    return _delegateArr;
}

#pragma mark 对外方法
/**
 单例模式
 
 @return self
 */
+(MoreDelegateManager *)shareInstance{
    static id instanc;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instanc=[[self alloc] init];
    });
    return instanc;
}
/**
 注册代理
 
 @param obj 需要实现代理的对象
 */
-(void)registerDelegate:(id)obj{
    NSLog(@"注册代理类:%@",NSStringFromClass([obj class].class));
    [self.delegateArr addPointer:(__bridge void*)obj];
}


/**
 解除代理
 
 @param obj 需要接触代理的对象
 */
-(void)unRegisterDelegate:(id)obj{
    NSLog(@"解除注册代理类:%@",NSStringFromClass([obj class].class));
    NSUInteger index=[self indexOfDelegte:obj];
    if (index!=NSNotFound) {
        [self.delegateArr removePointerAtIndex:index];
    }
    //去掉数组里面的野指针
    [self.delegateArr compact];
}

/**
 执行代理方法
 
 @param aSelector 类方法选择器
 */
-(void)delegateAction:(SEL)aSelector{
    for (id delegate in self.delegateArr) {
        if (delegate&&[delegate respondsToSelector:aSelector]) {
//            ((void (*) (id, SEL)) objc_msgSend) (delegate, aSelector);
            [delegate testMoreDelegate];
        }
    }
}

#pragma mark 内部使用方法

/**
 计算Delegate的下标

 @param obj 代理委托类
 @return 下标
 */
-(NSUInteger)indexOfDelegte:(id)obj{
    for (NSUInteger i=0; i<self.delegateArr.count; i++) {
        if ([_delegateArr pointerAtIndex:i]==(__bridge void*)obj) {
            return i;
        }
    }
    return NSNotFound;
}
@end

 

//使用

//在几个不同的控制器中注册代理
[[MoreDelegateManager shareInstance] registerDelegate:self];


//调用事件
[[MoreDelegateManager shareInstance] delegateAction:@selector(testMoreDelegate)];

//实现委托方法
-(void)testMoreDelegate{
    NSLog(@"代理实现类:%@",NSStringFromClass(self.class));
}