【浅析iOS中常用设计模式】

2018-06-01  本文已影响88人  大基本功

单例模式

单例模式是iOS开发中常用的一种设计模式,目的在于创建一次对象,多地方使用。
系统中的单例例如:UIApplication、 NSUserDefaults 等,开发中我们可以用作用户管理。

单例在ARC
#import "Tools.h"

@implementation Tools
// 创建静态对象 防止外部访问
static Tools *_instance;
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//    @synchronized (self) {
//        // 为了防止多线程同时访问对象,造成多次分配内存空间,所以要加上线程锁
//        if (_instance == nil) {
//            _instance = [super allocWithZone:zone];
//        }
//        return _instance;
//    }
    // 也可以使用一次性代码
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (_instance == nil) {
            _instance = [super allocWithZone:zone];
        }
    });
    return _instance;
}
// 为了使实例易于外界访问 我们一般提供一个类方法
// 类方法命名规范 share类名|default类名|类名
+(instancetype)shareTools
{
    //return _instance;
    // 最好用self 用Tools他的子类调用时会出现错误
    return [[self alloc]init];
}
// 为了严谨,也要重写copyWithZone 和 mutableCopyWithZone
-(id)copyWithZone:(NSZone *)zone
{
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
    return _instance;
}
单例在MRC
-(oneway void)release
{
    
}
-(instancetype)retain
{
    return _instance;
}
-(NSUInteger)retainCount
{
    return MAXFLOAT;
}
单例宏
#if __has_feature(objc_arc)
//如果是ARC,那么就执行这里的代码1
#else
//如果不是ARC,那么就执行代理的代码2
#endif
#define singleH(name) +(instancetype)share##name;

#if __has_feature(objc_arc)

#define singleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
    static dispatch_once_t onceToken;\
    dispatch_once(&onceToken, ^{\
        _instance = [super allocWithZone:zone];\
    });\
    return _instance;\
}\
\
+(instancetype)share##name\
{\
    return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
    return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
    return _instance;\
}
#else
#define singleM static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)shareTools\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
    return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
    return MAXFLOAT;\
}
#endif

参考

代理模式

当一个类的某些功能需要被别人来实现,但是既不明确是些什么功能,又不明确谁来实现这些功能的时候,委托模式就可以派上用场。例如你可以再写个类,实现也是完全可以的。换谁来,只要它实现了这个方法,我就可以委托它来做这个事。说到底一切都是为了使类之间的耦合性更松散。好的代码应该对扩展开放,对修改关闭。

1.委托者的.h文件中的代理指定以及声明

#import <UIKit/UIKit.h>
#import "LeftBarModel.h"
@protocol LeftBodyCellDelegate <NSObject>
// 由于这里没有任何修饰词所以是默认的代理方法,切记默认的代理方法,如果遵守了协议那就必须实现 
/**
*  @Description item选中跳转对应的控制器 
*  @param 被点击的按钮
*/
- (void)selectedItemButton:(NSInteger)index;
// 只是为了演示这儿是想说明下边这个方法是可选的就是可以实现也可以不实现
@optional
- (void)optionalFouction;
@end
@interface LeftBodyTableViewCell : UITableViewCell
// 在这里定义一个属性,注意这里的修饰词要用weak
@property(nonatomic,weak)id<LeftBodyCellDelegate>   leftBodyCellDelegate;
@end

2.委托者的.m文件中调用代理中的方法

// 这里解释下:我的这个委托者是定制的cell这里包含了一个按钮,按钮的点击事件在cell里我打算用代理传递出去给控制器,所以这是个按钮的点击响应事件
- (void)itemButonClick:(UIButton *)button
{
// 这里的self.leftBodyCellDelegate是判断一下控制器有没有遵守协议设置代理
// [self.leftBodyCellDelegate respondsToSelector:@selector(selectedItemButton:)]这个是判断有没有实现这个代理方法
// 当两个条件都满足了就可以调用代理方法了
  if (self.leftBodyCellDelegate && [self.leftBodyCellDelegate respondsToSelector:@selector(selectedItemButton:)])
  {
// 调用代理方法
      [self.leftBodyCellDelegate selectedItemButton:button.tag - 1000];
  }
}

3.代理控制器中的代码

// 首先是要遵守这个协议
@interface RootViewController ()<UITableViewDelegate,UITableViewDataSource,LeftBodyCellDelegate>
// 然后是要设置代理,在创建cell时候要设置代理人为控制器
cell.leftBodyCellDelegate = self;

4.实现协议中规定的方法了

#pragma mark --------------- LeftBodyCellDelegate
- (void)selectedItemButton:(NSInteger)index
{
// 这里是你点击了cell里的某个按钮后要做的操作
}

参考

观察者模式

KVO全称叫Key Value Observing,顾名思义就是一种观察者模式用于监听属性的变化,KVO和NSNotification有很多相似的地方,用addObserver:forKeyPath:options:context方法 去观察,用removeObserver:forKeyPath:context去移除观察者,用observeValueForKeyPath:ofObject:change:context:去响应观察者

KVO监听属性的变化非常方便,下面举个例子
//自定义MyTimer类,在.h文件中定义一个属性name
@property (nonatomic, strong) NSString *name;
//自定义ViewController,在controller的.h文件中也定义一个属性myView
@property (nonatomic, strong) UIView *myView;
在Viewcontroller的.m文件中定义一个button,设置点击事件,在该事件中分别调用上面定义的两个属性
int i = 5;
int sum = 15;
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
        btn.frame = CGRectMake(100, 100, 100, 30);
        [btn setTitle:@"点击" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(handleTimer:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:btn];
    
    
    _label = [[UILabel alloc ] initWithFrame:CGRectMake(100, 200, 180, 30)];
    _label.text = @"当前年龄15岁";
    [self.view addSubview:_label];
  
    //创建Mytimer对象
    _ourTimer = [[MyTimer alloc ] init];
    //观察属性name
    [_ourTimer addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew || NSKeyValueChangeOldKey context:nil];
    //观察属性myView
    [self addObserver:self forKeyPath:@"myView" options:NSKeyValueObservingOptionNew || NSKeyValueChangeOldKey context:nil];
    
}
点击事件,分别调用属性
- (void)handleTimer:(UIButton *)btn {
    _ourTimer.name = @"小明";
    self.myView = nil;
    NSLog(@"第一次设置名字");
}
一旦属性被操作了,这里会自动响应(上面设置观察的属性才会在这响应)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"name"]) {
        NSLog(@"名字发生了改变");
        _label.text = [NSString stringWithFormat:@"当前年龄%d岁", i + sum];
        sum = i + sum;
    } else if ([keyPath isEqualToString:@"myView"]) {
        NSLog(@"我的视图");
    }
}
移除
- (void)dealloc {
    [_ourTimer removeObserver:self forKeyPath:@"name"];
    [self removeObserver:self forKeyPath:@"myView"];
    
}

参考

补充:KVO的实现原理

简单概括:
KVO系统的处理是通过运行时,替换了对象的isa指针对象,并重写了set等方法

#import <Foundation/Foundation.h>
@interface NSObject (KVO)
- (void)xmg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:( void *)context;
@end

#import "NSObject+KVO.h"
#import <objc/message.h>
@implementation NSObject (KVO)
- (void)xmg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    /*
     1.runtime动态生成Person的子类(派生类)
     2.重写KVO_Person的属性set方法,目的:监听属性有没有改变
     3.修改对象的isa指针.
     */
    // 修改isa
    object_setClass(self, NSClassFromString(@"NSKVONotifying_Person"));
    
    // 保存观察者对象
    // self -> P;
    // 动态添加属性
    objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
#import "Person.h"
@interface NSKVONotifying_Person : Person
@end

#import "NSKVONotifying_Person.h"
#import <objc/message.h>

@implementation NSKVONotifying_Person
- (void)setAge:(NSInteger)age
{
    // super:标志,去执行父类方法
    [super setAge:age];
    
    // 调用观察者的observeValueForKeyPath
    id observer = objc_getAssociatedObject(self, "observer");
    
    [observer observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];
    
    NSLog(@"执行NSKVONotifying_Person");
//    NSLog(@"%@",[super class]);
    
}
@end

通知

个人理解相当于一个广播,由广播中心发出,有收听的人会收到广播所发出的消息

直接上代码

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notice:) name:@"tongzhi" object:nil];

-(void)notice:(id)sender{  
  NSLog(@"%@",sender);
}
//创建通知对象
NSNotification *notification = [NSNotification notificationWithName:@"tongzhi" object:nil];
 //Name是通知的名称 object是通知的发布者(是谁要发布通知,也就是对象) userInfo是一些额外的信息(通知发布者传递给通知接收者的信息内容,字典格式)
//    [NSNotification notificationWithName:@"tongzhi" object:nil userInfo:nil];
//发送通知
 [[NSNotificationCenter defaultCenter] postNotification:notification];

- (void)dealloc {
  //删除根据name和对象,如果object对象设置为nil,则删除所有叫name的,否则便删除对应的
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"tongzhi" object:nil];
}
上一篇下一篇

猜你喜欢

热点阅读