1. 什么是单例模式?php
在iOS应用的生命周期中,某个类只有一个实例。设计模式
2. 单例模式解决了什么问题?api
想象一下,若是咱们要读取文件配置信息,那么每次要读取,咱们就要建立一个文件实例,而后才能获取到里面的相关配置信息,这样若是,咱们若是要屡次读取这个文件的配置信息,那就要建立多个实例,这样严重浪费了内存资源。而实际应用中,当咱们要用到的类多是要反复用到的,通常能够考虑使用单例模式。这样能够大大下降建立新实例带来的内存浪费。数组
3. 单例模式的实现原理浏览器
通常会封装一个静态属性,并提供静态实例的建立方法(该方法使用GCD技术保证了整个程序生命周期只运行一次:用了dispath_once()函数)。网络
4. 应用实例架构
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"www.baidu.com"]]; NSFileManager *FileManager = [NSFileManager defaultManager];
注解:APP单例应用的建立,并在浏览器中打开URL地址。第二则是获取文件管理者,整个程序运行周期内,只有一个文件管理者,第一次建立后,之后要用到就直接用不会再建立了。app
1. 什么是委托模式? 框架
2. 委托模式解决了什么问题?ide
委托时为了下降一个对象的复杂度和耦合度,使其主要框架类可以具备通用性,其余旁枝末节的方法留给委托对象去实现。
3. 委托模式的实现原理
4. 应用实例
UITextFieldDelegate
#import "ViewController.h" @interface ViewController () <UITextFieldDelegate> @property (strong, nonatomic) UITextField *textField; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.textField.delegate = self; } @end
注解:控制器遵循协议<UITextFieldDelegate>,而且成为了textField的代理。这样控制器就拥有了协议中的方法,textField也就能让代理为本身作一些事。
1. 什么是观察者模式?
观察者模式也叫发布/订阅模式。好比订阅天气预报,其中有以下三个角色:
第一步:手机用户订阅中国移动短信中心的天气预报业务。
第二步:下雨时,气象局发布通告信息给中国移动短信中心:“有雨”。
第三步:手机用户就会收到中国移动短信中心的信息:“有雨”,接着用户就会知道应该采起什么的动做:“出门带伞”。
第四步:当不须要此项功能服务时,取消订阅。
气象局与用户之间的通讯是匿名的,用户只知道是中国移动发的短息,不知道气象局的存在。
2. 观察者模式解决了什么问题?
3. 观察者模式的实现原理
有4个角色:
4. 观察者模式的应用
主要有两种,通知机制和KVO机制。
气象台:
// observatory.h #import <Foundation/Foundation.h> @interface Observatory : NSObject @property (getter=isRain) BOOL isRain; @end
// observatory.m #import "Observatory.h" @implementation Observatory @end
手机用户:
// phoneUser.h #import <Foundation/Foundation.h> @interface PhoneUser : NSObject - (void)takeUmbrella; @end
// phoneUser.m #import "PhoneUser.h" @implementation PhoneUser - (void)takeUmbrella { NSLog(@"Take Umbrella"); } @end
测试用例:
// main.m #import <Foundation/Foundation.h> #import "Observatory.h" #import "PhoneUser.h" static NSString * const weatherForcast = @"weatherForecast"; int main(int argc, const char * argv[]) { // 用户订阅天气预报 PhoneUser *pUser = [[PhoneUser alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:pUser selector:@selector(takeUmbrella) name:weatherForcast object:nil]; // 气象台发布下雨天气预报 Observatory *observatory = [[Observatory alloc] init]; observatory.isRain = YES; if (observatory.isRain) { [[NSNotificationCenter defaultCenter] postNotificationName:weatherForcast object:observatory userInfo:nil]; } // 取消订阅 [[NSNotificationCenter defaultCenter] removeObserver:pUser name:weatherForcast object:observatory]; return 0; }
运行结果:
2016-10-25 03:12:36.860998 02-通知机制[5172:2936788] Take Umbrella Program ended with exit code: 0
注解:手机用户向服务中心预订天气预报服务,当下雨时,气象局就会发送通告给服务中心,服务中心收到通告就会发送短信给全部已订阅的用户,全部已订阅用户收到短信后则能够做出相应的动做。
股票后台数据:
// BackgroundData.h #import <Foundation/Foundation.h> @interface BackgroundData : NSObject /** 股价涨 */ @property (getter=isShareRise) BOOL isShareRise; @end
// BackgroundData.m #import "BackgroundData.h" @implementation BackgroundData @end
前台展现股票数据:
// foregroundDisplay.h #import <Foundation/Foundation.h> @interface ForegroundDisplay : NSObject @end
// foregroundDisplay.m #import "ForegroundDisplay.h" @implementation ForegroundDisplay #pragma mark - 当被观察者的属性值发送变化时调用改方法 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"The shares rise"); } @end
测试用例:
// main.m #import <Foundation/Foundation.h> #import "BackgroundData.h" #import "ForegroundDisplay.h" int main(int argc, const char * argv[]) { // 前台展现股票跌涨 ForegroundDisplay *fgDisplay = [[ForegroundDisplay alloc] init]; // 后台数据 BackgroundData *bgData = [[BackgroundData alloc] init]; bgData.isShareRise = NO; // 为后台数据注册观察者 [bgData addObserver:fgDisplay forKeyPath:@"isShareRise" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil]; // 后台数据属性值改变 bgData.isShareRise = YES; // 移除观察者 [bgData removeObserver:fgDisplay forKeyPath:@"isShareRise" context:nil]; return 0; }
运行结果:
2016-10-25 02:51:35.931473 03-KVO机制[5059:2820557] The shares rise Program ended with exit code: 0
注解:注册一个观察者观察后台股票数据的变更,一旦有变更,则会触发观察者的特定方法,在改方法中能够配置后台数据与前台展现同步。最后应当移除观察者,若是没有移除,则至关于后台数据类销毁了,但观察者还保存着对它的引用,这就会奔溃。
1. 什么是MVC模式
所谓MVC模式,即model,view,controller。模型、视图、控制器,其中各自有其职能所在:
Controller能够直接与Model和View进行通讯,而View不能和Controller直接通讯。View与Controller通讯须要利用代理协议的方式,当有数据更新时,Model也要与Controller进行通讯,这个时候就要用Notification和KVO,这个方式就像一个广播同样,Model发信号,Controller设置监听接受信号,当有数据更新时就发信号给Controller,Model和View不能直接进行通讯,这样会违背MVC设计模式。
2. MVC模式解决了什么问题?
MVC模式可以完成各司其职的任务模式,因为下降了各个环节的耦合性,大大优化Controller的代码量,便于调试与维护,并且还利于程序的可复用性 。有利于团队合做。
3. MVC的实现原理
引用一张MVC通讯示例图:
其中虚实线就比如交通规则路线,虚线表示可穿越,实线表示不可穿越。从图中能够看出:
4. MVC模式的应用
用MVC模式构建以下图片所展现的demo,其中的数据都是经过网络请求加载的,cell是自定义的。
1. 以MVC划分文件
2. 控制器
1 // LKSubTableViewController.h 2 #import <UIKit/UIKit.h> 3 4 @interface LKSubscriptionTableViewController : UITableViewController 5
6 @end
1 // LKSubscriptionTableViewController.m 2 #import "LKSubscriptionTableViewController.h" 3 #import "LKSubscriptionItem.h" 4 #import "LKSubscriptionTableViewCell.h" 5 6 #import <AFNetworking/AFNetworking.h> 7 #import <MJExtension/MJExtension.h> 8 #import <UIImageView+WebCache.h> 9 #import <SVProgressHUD-0.8.1/SVProgressHUD.h> 10 11 /** 可重用单元标识 */ 12 static NSString *cellIdentifier = @"Cell"; 13 14 @interface LKSubscriptionTableViewController () 15 /** 会话管理者 */ 16 @property (strong, nonatomic) AFHTTPSessionManager *mgr; 17 /** 网络数据 */ 18 @property (strong, nonatomic) NSArray *data; 19 20 @end 21 22 @implementation LKSubscriptionTableViewController 23 24 - (void)viewDidLoad { 25 [super viewDidLoad]; 26 27 // 注册 28 [self.tableView registerNib:[UINib nibWithNibName:@"LKSubscriptionTableViewCell" bundle:nil] forCellReuseIdentifier:cellIdentifier]; 29 30 31 // 加载网络数据 32 [self loadData]; 33 34 [SVProgressHUD showWithStatus:@"加载中..."]; 35 36 } 37 38 #pragma mark - 1. 数据源方法 39 40 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 41 42 return 1; 43 } 44 45 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 46 47 return self.data.count; 48 } 49 50 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 51 // 加载自定义的表单元 52 LKSubscriptionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 53 54 // 获取模型 55 LKSubscriptionItem *item = self.data[indexPath.row]; 56 57 // 控制器经过接口传递模型数据给视图 58 cell.item = item; 59 60 return cell; 61 } 62 63 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 64 return 80; 65 } 66 67 68 #pragma mark - 2. 加载网络数据 69 - (void)loadData { 70 // 建立会话管理者 71 self.mgr = [AFHTTPSessionManager manager]; 72 73 // 封装参数 74 NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; 75 parameters[@"a"] = @"tag_recommend"; 76 parameters[@"c"] = @"topic"; 77 parameters[@"action"] = @"sub"; 78 79 // 发送请求 80 [self.mgr GET:@"http://api.budejie.com/api/api_open.php" parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 81 82 [SVProgressHUD dismiss]; 83 // 经过字典数组来建立一个模型数组 84 self.data = [LKSubscriptionItem mj_objectArrayWithKeyValuesArray:responseObject]; 85 86 // 刷新列表显示到界面 87 [self.tableView reloadData]; 88 89 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 90 NSLog(@"加载推荐标签出错:%@", error); 91 [SVProgressHUD showErrorWithStatus:@"加载失败,请检查网络是否正常"]; 92 [SVProgressHUD dismiss]; 93 }]; 94 } 95 96 97 - (void)viewWillDisappear:(BOOL)animated { 98 [super viewWillDisappear:animated]; 99 [SVProgressHUD dismiss]; 100 [self.mgr.tasks makeObjectsPerformSelector:@selector(cancel)]; 101 } 102 103 104 @end
2. 视图
1 // LKSubscriptionTableViewCell.h 2 #import <UIKit/UIKit.h> 3 4 @class LKSubscriptionItem; 5 6 @interface LKSubscriptionTableViewCell : UITableViewCell 7 /** 模型数据 */ 8 @property (strong, nonatomic) LKSubscriptionItem *item; 9 10 @end
1 // LKSubscriptionTableViewCell.m 2 #import "LKSubscriptionTableViewCell.h" 3 #import "LKSubscriptionItem.h" 4 5 #import <UIImageView+WebCache.h> 6 7 8 @interface LKSubscriptionTableViewCell () 9 /** 订阅图标 */ 10 @property (weak, nonatomic) IBOutlet UIImageView *icon; 11 12 /** 订阅名称 */ 13 @property (weak, nonatomic) IBOutlet UILabel *name; 14 15 /** 订阅人数 */ 16 @property (weak, nonatomic) IBOutlet UILabel *number; 17 18 @end 19 20 @implementation LKSubscriptionTableViewCell 21 22 #pragma mark - 根据控制器传递过来的模型数据来显示cell 23 - (void)setItem:(LKSubscriptionItem *)item { 24 _item = item; 25 26 // 设置订阅名称 27 _name.text = item.theme_name; 28 29 // 设置订阅人数 30 [self resolveNum]; 31 32 // 设置订阅图标 33 [_icon sd_setImageWithURL:[NSURL URLWithString:self.item.image_list] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]]; 34 35 } 36 37 #pragma mark - 处理订阅数字 38 - (void)resolveNum { 39 NSString *numStr = [NSString stringWithFormat:@"有%@人订阅",self.item.sub_number]; 40 NSInteger num = self.item.sub_number.integerValue; 41 if (num > 10000) { 42 CGFloat numF = num / 10000.0; 43 numStr = [NSString stringWithFormat:@"%.1f万人订阅",numF]; 44 numStr = [numStr stringByReplacingOccurrencesOfString:@".0" withString:@""]; 45 } 46 47 _number.text = numStr; 48 } 49 50 #pragma mark - 处理cell间的分割线 51 - (void)setFrame:(CGRect)frame { 52 frame.size.height -= 1; 53 // 才是真正去给cell赋值 54 [super setFrame:frame]; 55 } 56 57 #pragma mark - 处理切割图片为圆形头像 58 - (void)awakeFromNib { 59 [super awakeFromNib]; 60 61 self.icon.layer.cornerRadius = 30; 62 self.icon.layer.masksToBounds = YES; 63 } 64 65 66 @end
3. 模型数据
1 // LKSubscriptionItem.h 2 #import <Foundation/Foundation.h> 3 4 @interface LKSubscriptionItem : NSObject 5 /** 图标 */ 6 @property (strong, nonatomic) NSString *image_list; 7 8 /** 名称 */ 9 @property (strong, nonatomic) NSString *theme_name; 10 11 /** 订阅人数 */ 12 @property (assign, nonatomic) NSString *sub_number; 13 14 @end
// LKSubscriptionItem.m #import "LKSubscriptionItem.h" @implementation LKSubscriptionItem @end
注解:经过上面的demo能够知道:控制器主要负责将从网络上请求数据获得的数据经过接口存储到数据模型之中,当view,也即便自定义的cell须要数据时,并协助view与model之间的通讯,也就是把model的数据传给了cell,这样cell就能及时显示数据给用户。上述demo中各角色职责分明: