美文网首页面试iOSiOS 砖家纪实录
【iOS基础】设计模式知多少

【iOS基础】设计模式知多少

作者: 亲爱的大倩倩 | 来源:发表于2016-07-29 17:51 被阅读1629次

    什么是设计模式
    设计模式是为特定场景下的问题定制的解决方案,设计模式分三种类型:创建型,结构型,行为型.这里只列举iOS常用到的几种:

    **创建型 : **单例 , 工厂
    **结构型 : **代理 , MVC
    **行为型 : **观察者 , 策略

    单例模式
    单例类 : 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例

    优点:
    1-节省内存
    每次获取实例时会先进行判断,实例存在则返回,否则创建实例,如果一直不用,则不会创建实例,从而节省了内内存空间

    2-因为单例类控制实例化过程,所以可以更灵活的修改实例化过程

    缺点:
    1-运行费时间
    上面的判断会浪费一些时间

    2-线程安全问题
    并发情况下,如果线程A和线程B同时调用某一方法,会创建出两个实例,此时单例模式失效,若想解决线程安全问题,需要加synchronized解决,但会降低访问速度

    使用场景:
    1-如果创建某一个对象会耗费很多系统资源,此时采取单例模式,因为只需要一个实例,会节省alloc时间

    2-若很多模块需要使用同一变量,可以将它放入单例类

    3-实际应用:"我的音乐"项目中 "音乐播放器/列表管理/文件管理"均为单例模式

    1.定义
    + (instancetype)defaultManager
    {
        static 单例类名 *instance = nil;
        static dispatch_once_t onceToken;
        
        dispatch_once( &onceToken,
                      ^{
                          instance = [[self alloc] init];
                      });
        return instance;
    }
    
    2.使用
    [单例类名 defaultManager]调用方法及属性即可
    
    3.注意
    假如我们令
    _yourList = [CCListManager defaultManager].defaultList;
    若你对_yourList执行移除操作
    [_yourList removeObjectAtIndex:indexPath.row];
    则单例类中的defaultList此行也被移除了
    

    代理模式

    当一个类的某些功能(协议)需要由别的类来实现,但是又不确定具体会是哪个类实现,本质是某个类持有了另一个类的指针
    协议是约束一个类必须实现某些方法
    协议中只能定义方法,不能定义成员变量,属性,@required为必须实现的,@optional为可选择实现的
    因为协议这个接口和类并不存在关联关系,所以我们要用到代理,声明一个代理属性,约定实现代理的对象去实现协议方法
    说起来有点绕,但理解了就好了....

    优点:
    1-有利于代码的封装
    2-有利于程序的结构化和层次化
    3-若是@required的方法,没有实现会编译警告/报错
    4-一个类可以定义不同的协议,当然,每个协议得对应不同的delegate

    缺点:
    1-代码量多,协议定义,delegate属性,本身决定什么时候执行代理,实现代理类的实现

    2-释放后,delegate要为nil,否则会是野指针,造成内存泄漏,这也是要用weak来声明delegate的原因

    3-只能一对一通信,不过这个好像不算是缺点
    备注:
    -假如现在有类A,类B,类C
    -类C协议为CDelegate,点击类C的按钮时,相应协议,输出一句话
    -类A和类B都实现了<CDelegate>方法
    -执行的操作是,从类A跳转到类B,从类B跳转至类C,点击类C的按钮,只有类B相应了

    使用场景:
    tableView的Cell内按钮点击时
    音乐播放器当前歌曲的进度
    ......

    实现过程:
    举例:类A实现了类B的协议
    类B
    1.声明协议及协议方法,这是要让别的类,比如“A”实现的

    @protocol BDelegate <NSObject>
    - (void)passValue: (NSString *)string;
    @end
    

    2.声明此协议的代理对象,谁使用了这个代理对象,谁就实现上面的协议,比如“A”

    @property(nonatomic,assign)id< BDelegate > delegate;
    

    3.本类决定了,实现我代理对象的类,比如“A”,何时实现我的协议方法,例当我点击我本身的按钮时实现

    -(void)onClick
    {
            [self.delegate passValue:@"少壮不努力,老大徒伤悲啊”];   
    }
    

    类A
    1.声明”B”对象,并实现”B”协议的代理

    BViewController *b = [[BViewController alloc] init];
    b.delegate = self;
    

    2.实现协议方法,何时实现由”B”控制

    - (void)passValue:(NSString *)string
    {
            NSLog(@"代理传给了我什么%@",string);
    }
    

    拿tableView的cell内按钮点击举例

    1.在cell中声明协议,代理对象,及何时响应
    //声明协议
    @class myCell;
    @protocol FQDelegate <NSObject>
    - (void)clickDelegateButton:(myCell *)cell;
    @end
    
    //声明代理对象
    @interface myCell : UITableViewCell
    //这个名字一般叫delegate,但其实你爱叫什么叫什么
    @property (weak, nonatomic) id<FQDelegate> del;
    @end
    
    //何时响应
    - (IBAction)onClickButton:(id)sender
    {
            if(self.del && [self.del respondsToSelector:@selector(clickDelegateButton:)])
            {
                [self.del clickDelegateButton:self];
            }
    }
    
    2.tableView实现
    @interface ViewController ()<UITableViewDataSource,UITableViewDelegate,FQDelegate>
    
    - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *identifier = @"myCell";
        myCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        if (!cell)
        {
            cell = [[[NSBundle mainBundle] loadNibNamed:@"myCell" owner:self options:nil]lastObject];
            //点击事件
            cell.del = self;
        }
        //绘制cell内容
        //姓名
        cell.nameLabel.text = @"亲爱的大倩倩";
        
        //双拼
        NSMutableString *mutStr = [NSMutableString string];
        [mutStr appendString:@"双"];
        [mutStr appendString:@" / "];
        [mutStr appendString:@"拼"];
        cell.doubleLabel.text = mutStr;
        
        return cell;
    }
    
    - (void)clickDelegateButton:(myCell *)cell
    {
        NSIndexPath *indexPath = [_mainTableView indexPathForCell:cell];
        NSLog(@"你当前点击的是第几行%ld",(long)indexPath.row);
    }
    

    扩展一:运行时机制
    假如我们上面不用代理实现,改成如下依然可以实现:

    1.cell中
    @property (nonatomic,weak) id delegate;
    @property (nonatomic,assign) SEL method;
    
    - (IBAction)onClickButton:(id)sender
    {
        if ([self.delegate respondsToSelector:self.method])
        {
            [self.delegate performSelector:self.method withObject:self];
        }
    }
    
    2.tableView实现
    - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *identifier = @"myCell";
        myCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        if (!cell)
        {
            cell = [[[NSBundle mainBundle] loadNibNamed:@"myCell" owner:self options:nil]lastObject];
            //点击事件
            cell.delegate = self;
            cell.method = @selector(onClickMainButton:);
        }
        return cell;
    }
    
    - (void)onClickMainButton:(myCell *) cell
    {
        NSIndexPath *indexPath = [_mainTableView indexPathForCell:cell];
        NSLog(@"你点击的是第几行%ld",(long)indexPath.row);  
    }
    

    扩展二
    1-代理用weak,不用assign的原因
    assign和weak均是指针赋值(直接赋值),不改变索引计数,但当被销毁时,
    assign:如果使用完毕,不将其置为nil,会产生野指针,操作不当会崩溃
    weak:在属性所指的对象遭到摧毁时,属性值也会清空(nil out),不会崩溃

    假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a 和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉

    2- assigin可以用非 OC 对象,而 weak 必须用于 OC 对象

    3- 代理用weak,不用strong的原因,是因为防止循环引用

    观察者模式

    观察者模式是一种通知变化的模式,分下面两种

    • Notification
      优点:
      1-代码量少,实现简单
      2-对于一个发出的通知,多个对象能够做出反应,即一对多
      缺点:
      1-释放通知对象时,需要在通知中心移除注册
      2-观察者需要提前知道通知的名字,如果未定义,会不同步
      3-编译器不会检查通知是否能被观察者处理(相对比delegate)

    • KVO
      优点:
      1-简单
      2-能够对非我们创建的对象,即内部对象的状态改变做出相应
      3-用key path观察属性,因此也可以观察嵌套对象
      4-也可以一对多
      缺点:
      1-观察的属性必须使用NSString定义

    • Delegate,NSNotification,KVO
      1-三者均是类和类之间的通信
      2-NSNotification和KVO一对多,Delegate一对一
      3-NSNotification和KVO不关心接受者的态度,意思就是发出通知后剩下的事就不管了,而delegate会关注结果

    NSNotification例子(带参数)

    1.声明消息
    [[NSNotificationCenter defaultCenter] addObserver:self
                                        selector:@selector(respondNotification:) name:@“FQNotification" object:nil];
    
    2.响应消息
    [[NSNotificationCenter defaultCenter] postNotificationName:@“FQNotification”object:@“llama"];
    
    3.实现消息
    - (void)respondNotification:(NSNotification*)aNotification
        {
           NSString *number= [aNotification object];
          NSLog(@"接收到的内容是%@",number);
        }
    
    

    KVO会单开专题讲解

    MVC模式
    Model-View-Controller的缩写,是一种架构模式,讲的是M和V的代码分离,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。

    优点:
    1-层次清晰,代码简洁
    2-方便测试及改动,易于维护
    缺点:
    1-增加系统结构和实现的复杂性
    2-视图对模型数据低效率访问

    这个就不举例子了.

    工厂模式

    简单工厂模式: 为了向客户提供方便,将分配和初始化合在一个步骤中,返回被创建的对象.说白了,就是对象的封装.
    优点:客户端直接使用产品,而不用关心产品的具体实现
    缺点:工厂类负责逻辑实现,一旦出现问题,均受影响,而且,每增加一个产品,就要增加一个工厂类
    使用场景:多个地方用到某一对象,并且此对象属性一样时

    简单工厂模式例子:
    每个应用程序都会有自己的主色调,字体颜色及大小等等吧,我们定义每一个Button时,若都写给它设置颜色,字体,字号等太麻烦,可以封装起来,这样声明一个Button只需要执行一句代码就行啦

    1.定义一个工具类,里面封装了各种,这里只拿button举例
    @interface ToolViewController : UIViewController
    #pragma mark - Button 
    + (UIButton *)buttonWithFrame:(CGRect)frame  buttonWithTitle:(NSString *)title buttonWithSuperView:(UIView *)view;
    
    
    + (UIButton *)buttonWithFrame:(CGRect)frame  buttonWithTitle:(NSString *)title buttonWithSuperView:(UIView *)view
    {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        button.frame = frame;
        button.backgroundColor = [UIColor colorWithRed:69/255.0 green:187/255.0 blue:237/255.0 alpha:1];
        [button setTitle:title forState:UIControlStateNormal];
        [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [button.layer setCornerRadius:5.0];
        [view addSubview:button];
        return button;
    }
    
    2.在你需要的ViewController里,初始化对象
    #import "ToolViewController.h"
    ToolViewController *nextStepButton = [ToolViewController buttonWithFrame:CGRectMake(10, 200, [UIScreen mainScreen].bounds.size.width - 20, 44)  buttonWithTitle:@"下一步" buttonWithSuperView:self.view];
    
    3.一句话就搞定啦,如果设计突然变了颜色,只需要在工具类里改一句话就行啦
    

    类工厂模式:大致可以理解为,通过继承,创建不同的对象
    优点:方便替换
    缺点:不能算缺点,只是创建的这些类是同一个父类
    使用场景:若使用时类会有变动,比如后台若传你不同的数据,需要建不同的类,可以考虑用这个

    1.父类为BaseClass,有两个子类ONE和TWO,在父类中
    @interface BaseClass : NSObject
    + (instancetype)init;
    @end
    
    #import "ONE.h"
    #import "TWO.h"
    @implementation BaseClass
    + (instancetype)init
    {
        BaseClass *mainClass = nil;
        mainClass = [[ONE alloc] init];
        return mainClass;
    }
    
    2.在ViewController中声明10个ONE对象(其实应该是在不同的地方声明)
    #import "BaseClass.h"
    - (void)viewDidLoad
    {
        [super viewDidLoad];
           for (int i = 0; i < 10; i++)
        {
            BaseClass *myClass = [BaseClass init];
            NSLog(@"你建的类为%@",myClass);
        }    
    }
    3.此时打印你会发现,你建的是10个ONE对象
    2016-07-29 15:05:07.985 基础[1382:93824] 你建的类为<ONE: 0x7fca21f7dc50>
    2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
    2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
    2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21e17630>
    2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21e17630>
    2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21e17630>
    2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
    2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
    2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
    2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
    
    4.现在我们需要将这10个ONE变成TWO,只需要在BaseClass中改一句话,方便快捷
    mainClass = [[TWO alloc] init];
    
    2016-07-29 15:09:46.111 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3f34490>
    2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3d2af50>
    2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
    2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
    2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3cbbbe0>
    2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3d2af50>
    2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
    2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
    2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
    2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
    
    

    再举一个例子,假如后台返回的数据有不同的类型,根据传来的key值不同返回不同的对象

    1.在BaseClass中
    @interface BaseClass : NSObject
    + (instancetype)initWithDictionary:(NSDictionary *)dictionary;
    @end
    
    #import "ONE.h"
    #import "TWO.h"
    @implementation BaseClass
    + (instancetype)initWithDictionary:(NSDictionary *)dic
    {
        BaseClass *mainClass = nil;
        
        if ([dic[@"key"] isEqualToString:@"ONE"])
        {
            mainClass = [[ONE alloc] init];
        }
        else
        {
            mainClass = [[TWO alloc] init];
        }
        return mainClass;
    }
    @end
    
    2.在ViewController中
    #import "BaseClass.h"
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        NSDictionary *dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:@"ONE",@"key",@"AAA",@"name",nil];
        NSDictionary *dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:@"TWO",@"key",@"BBB",@"name",nil];
        NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:dic1,dic2, nil];
        
        for (NSDictionary *dic in arr)
        {
            BaseClass *myClass = [BaseClass initWithDictionary:dic];
            NSLog(@"这个模型是%@",myClass);
        }
    }
    
    3.结果
    2016-07-29 15:13:58.714 基础[1544:107073] 这个模型是<ONE: 0x7fb450e0cda0>
    2016-07-29 15:13:58.715 基础[1544:107073] 这个模型是<TWO: 0x7fb450cae900>
    
    

    策略模式

    和工厂模式相比,策略模式是对算法的封装,使算法可以互相替换.

    之前看过一个例子,假如你出去旅游,有多种方案选择,可以骑自行车,汽车,火车,飞机,每个策略都可以得到相同的结果,但使用的是不同的资源

    有一篇讲策略模式很好的文章,推荐一下
    设计模式之策略模式(iOS开发,代码用Objective-C展示)

    相关文章

      网友评论

      本文标题:【iOS基础】设计模式知多少

      本文链接:https://www.haomeiwen.com/subject/rcbajttx.html