iOS开发里不用MVC反面教材
来源:互联网 发布:windows手写笔记软件 编辑:程序博客网 时间:2024/05/02 00:36
百度搜索MVC模式,一堆,说了一堆很多,反正就是各种好。我这个人表达能力不太强,反正我对这样的模式是认同的,优点也非常多,对代码的重构和维护都是非常好的实现模式。我以前吃过这样的亏,当时还没有不知道什么叫做MVC,当时自己学习做点东西都是各种字典,数组。今天我写一个亲身的例子,从相反的方向来说明下MVC的重要性。
背景:工作需要对界面的一个功能进行升级,原来的红包界面加入了可以叠加使用的红包,不再像以前一样一次只能使用一个,每个红包多了属性:是否可以叠加使用。
大致效果如下:
之前的界面只有展现了红包,点击后就会返回使用,现在增加一个功能,下面两个按钮开始状态分别是:不使用红包,合并红包。
点击合并后出现上面的样子,红色表示选中,灰色表示不选中,服务器端还没有完成,我先做个大概的样子模拟下。
我感觉也不难,无非就是逻辑上考虑点问题但是,事实却不是,原因是公司的代码是下面这样的,一起来看看,我相信我一定会记住的,希望以后都别这样写代码,简直是给别人挖坑。因为该界面还牵扯到其他的功能,有些地方看不懂,时间紧迫不敢重写。顺便说下这个界面代码几乎都没有注释,所有的注释都是我自己添加的。
这是服务器请求的方式和返回数据格式,采用了分页加载。
/** * user_id page页数(每页10个) 返回示例: 请求成功且有可用优惠券时: {"code":200,"message":"success","content": {"coupon_info":[ { "coupon_id":"323", "is_used":"2", "source":"商城发放", "give_time":"2015.10.12", "coupon_price":"1.5", "available_time":"23小时后可用" }, {"coupon_id":"25003", "is_used":"0", "source":"系统发放", "give_time":"2015.08.20 17:46", "expire_time":"2015.08.29 17:46", "coupon_price":"1.5" } ] } } 请求成功但没有可用的优惠券时: {"code":200,"message":"success","content":{"coupon_info":null}} 备注: is_used 0 为未使用, 2为不可用 */
下面是请求网络数据的函数、
- (void)loadHongBaoJson{ if (isNeedIndector) { [MBProgressHUD showHUDAddedTo:self.view animated:YES]; }else { [MBProgressHUD hideHUDForView:self.view animated:YES]; } emptyboxview.hidden = YES; NSMutableDictionary * mDic = [NSMutableDictionary dictionaryWithCapacity:0]; NSString * userID = USERDEFAULTS_GET(LEXIAO_USER_ID); userID = userID==nil?@"":userID; [mDic setValue:userID forKey:@"user_id"]; [mDic setObject:[NSString stringWithFormat:@"%d",hongBaoNowPage] forKey:@"page"]; [mDic setValue:@"get_coupon_info" forKey:@"action"]; [AFNetworkTool JSONDataWithUrl:LEXIAO_REQURL_SDQR withDic:mDic success:^(id json) { NSDictionary * resPonseObject = json; failureView.hidden = YES; hongBaoTable.hidden = NO; NSString * code = [NSString stringWithFormat:@"%@",[resPonseObject objectForKey:@"code"]]; code=code==nil?@"":code; //成功回调 if ([code isEqualToString:@"200"]) { emptyboxview.hidden = YES; [MBProgressHUD hideHUDForView:self.view animated:YES]; NSDictionary * YHQDic = [resPonseObject objectForKey:@"content"]; NSArray * newHongBao = [[NSArray alloc]init];//这里不用初始化吧,下面的那行没有意义吧 newHongBao = YHQDic[@"coupon_info"]; if (newHongBao!=nil&&![newHongBao isEqual:[NSNull null]]){ if ([allHongBao isEqual:[NSNull null]] || allHongBao == nil) { allHongBao = [[NSMutableArray alloc]init]; } [hongBaoTable.footer endRefreshing]; [hongBaoTable.header endRefreshing]; //根据服务器返回数据的个数进行处理 if ([newHongBao count] < 10) { hongBaoPage = hongBaoNowPage; refreshFooter.stateLabel.hidden = NO; [hongBaoTable.footer noticeNoMoreData]; } if ([newHongBao count] == 10) { hongBaoNowPage ++; refreshFooter.stateLabel.hidden = NO; } /** * 这里进行了函数跳转 */ [self reloadTableView:YHQDic]; }else{ emptyboxview.hidden = NO; [self setNavBarRightViewWithTitle: nil orImage:nil]; [hongBaoTable.footer endRefreshing]; [hongBaoTable.header endRefreshing]; } } } fail:^(id error){ [MBProgressHUD hideHUDForView:self.view animated:YES]; [hongBaoTable.footer endRefreshing]; [hongBaoTable.header endRefreshing]; emptyboxview.hidden = NO; [self setNavBarRightViewWithTitle: nil orImage:nil];// [hongBaoTable setHidden:YES];// hongBaoTable = nil;// [hongBaoTable removeFromSuperview];// [self loadSuDiEmptyBox];// [AFNetworkTool AlertWithTitle:nil andMsg:NSLocalizedString(@"lexiao_wangluolianjieshibai", nil)];// [self showWithCustomViewWithImage:@"chahao" Message:@"网络连接失败"]; //NSLog(@"失败~~~%@",error); if (allHongBao == nil || [allHongBao isEqual:[NSNull null]] || allHongBao.count <= 0) { [self networkConnectionFailureViewLoad]; failureView.reloadButton.selected = NO; } }];}
上面的代码没有什么问题,上面备注“进行了函数跳转地方是这个样子的”,工程中类似这样的if判断,几乎每次在出现网络请求的时候都有,这样判断不下几百个,有的时候也太谨小慎微了,上面已经对YHQDic[@"coupon_info"]已经判断过了,不然也不会执行到这里,这里又进行if判断。还有NSNull类型的指针,目的是为什么呢,OC里面指针所指向的对象类型和指针类型有非常大的联系吗?实在不懂,下面这段代码就是有数据的时候就加载,没有的话就显示一个特殊的视图
- (void)reloadTableView:YHQDic{ NSNull *nu = YHQDic[@"coupon_info"];// BOOL isE = [nu isEqual:[NSNull null]]; if ([nu isEqual:[NSNull null]] || [YHQDic[@"coupon_info"] count] <= 0 || YHQDic[@"coupon_info"] == nil) { [hongBaoTable removeFromSuperview]; hongBaoTable = nil; emptyboxview.hidden = NO; [self setNavBarRightViewWithTitle: nil orImage:nil]; }else { [hongBaoTable setHidden:NO]; [emptyboxview setHidden:YES]; [self loadData:YHQDic];//这里又一次跳转 [hongBaoTable reloadData]; }}
当时我要是不看服务器端的文档,我都傻了,取字典里面的数组里面的字典的值,直接取了,一个注释都没有。下面的代码的意思就是,根据服务器返回的字典里面的数组里面的字典,在重新建立一个属于自己的字典。并添加到给tabbleviewcell显示的数组里面。
当时下面要添加到数组里面的字典用的是不可变,我自己kvc赋值还不行,最后改成动态的了,自己为了实现cell点击后状态的切换,
自己在他的那个字典里增加了一个字段isSelected,每次选择单元格要对那个这个key进行赋值。
- (void)loadData:(NSDictionary *)YHQDic{ [MBProgressHUD hideHUDForView:self.view animated:YES]; //图片列表 imageList = [[NSArray alloc]initWithObjects:@"1.5+@2x.png",@"1.1_1.5@2x.png",@"0.6_1.0@2x.png",@"0_0.5@2x.png",@"0_0.0@2x.png", nil]; NSArray *arr = YHQDic[@"coupon_info"]; //上面刚刚 NSArray *arr = YHQDic[@"coupon_info"]; 下面循环依旧用YHQDic,仅仅就为了计算count for (NSInteger i = 0; i < arr.count; i ++) { //这么多属性也没有考虑建立一个模型, NSString *is_use = YHQDic[@"coupon_info"][i][@"is_used"]; NSString *couponID = YHQDic[@"coupon_info"][i][@"coupon_id"]; NSString *source = YHQDic[@"coupon_info"][i][@"source"]; NSString *give_time = YHQDic[@"coupon_info"][i][@"give_time"]; NSString *expire_time1 = YHQDic[@"coupon_info"][i][@"expire_time"]; NSString *expire_time = [NSString stringWithFormat:@"过期时间:%@", expire_time1]; NSString *couponPrice = YHQDic[@"coupon_info"][i][@"coupon_price"]; CGFloat prize = [couponPrice floatValue]; NSString *backImage; /** * is_used 0 为未使用, 2为不可用 */ if ([is_use isEqualToString:@"2"]) { NSString *available_time = YHQDic[@"coupon_info"][i][@"available_time"]; //下面的这四个判断没有必要吧,都一样的结果。 if (prize <= 0.5) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[4]; }else if (prize > 0.5 && prize <= 1.0) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[4]; }else if (prize <= 1.5 && prize > 1) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[4]; }else if (prize > 1.5) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[4]; } NSMutableDictionary *dicForHB = [NSMutableDictionary dictionaryWithDictionary:@{@"coupon_id":couponID,@"source":source,@"give_time":give_time,@"expire_time":available_time,@"coupon_price":couponPrice,@"backImage":backImage,@"is_used":is_use,@"isSelected":@"NO"}]; [allHongBao addObject:dicForHB]; }else {//可用的红包 if (prize <= 0.5) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[3]; }else if (prize > 0.5 && prize <= 1.0) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[2]; }else if (prize <= 1.5 && prize > 1) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[1]; }else if (prize > 1.5) { couponPrice = [NSString stringWithFormat:@"%0.1f",prize]; backImage = imageList[0]; } NSMutableDictionary *dicForHB = [NSMutableDictionary dictionaryWithDictionary:@{@"coupon_id":couponID,@"source":source,@"give_time":give_time,@"expire_time":expire_time,@"coupon_price":couponPrice,@"backImage":backImage,@"is_used":is_use,@"isSelected":@"NO"}]; [allHongBao addObject:dicForHB]; } } self.combineArr = [NSMutableArray array];//因为allHongbao内容会变化,所以在变化的时候直接重写查找可以合并的红包 canCombineCnt =0; for (int i=0; i<allHongBao.count; i++) { [self.combineArr addObject:allHongBao[i]]; canCombineCnt ++; //} } if (canCombineCnt>0) { self.combineItem.enabled = YES; } else { self.combineItem.enabled =NO; } [MBProgressHUD hideHUDForView:self.view animated:YES];}
下面是点击单元格的方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ if (showMode==0) { NSString *hbJZ = allHongBao[indexPath.row][@"coupon_price"]; NSString *hongbaoID = allHongBao[indexPath.row][@"coupon_id"]; if (![allHongBao[indexPath.row][@"is_used"] isEqualToString:@"2"]) { for (UIViewController * tmp in self.navigationController.viewControllers) { if ([tmp isKindOfClass:[ConfirmExpressViewController class]]) { ConfirmExpressViewController * vc = (ConfirmExpressViewController *)tmp; //这里进行传值 vc.hongBaoJZ = hbJZ; vc.hongBaoID = hongbaoID; [self.navigationController popToViewController:vc animated:YES]; } } } } else if(showMode==1)//在合并模式下单元格的点击事件。 { HongBaoTableViewCell * cell = (HongBaoTableViewCell *)[tableView cellForRowAtIndexPath:indexPath]; NSString * str; if ([cell.isSelected isEqualToString:@"YES"]) { str =@"NO"; } else { str = @"YES"; } if ([str isEqualToString:@"YES"]) {//被选中了 [self.selectedArr addObject:self.combineArr[indexPath.row]];//添加否则就移除。 } else { [self.selectedArr removeObject:self.combineArr[indexPath.row]]; } NSDictionary * dict = self.combineArr[indexPath.row]; [dict setValue:str forKey:@"isSelected"]; [self.combineArr replaceObjectAtIndex:indexPath.row withObject:dict]; NSIndexPath *refreshCell = indexPath; [hongBaoTable reloadRowsAtIndexPaths:[NSArray arrayWithObjects:refreshCell, nil] withRowAnimation:UITableViewRowAnimationRight]; // [tableView reloadSections:idx withRowAnimation:UITableViewRowAnimationRight]; } //要添加保存数据的代码}
下面的showMode=0是原来的情况,按字典里面的键取值,给单元格赋值。
/** 红包单元格内容 */- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *cellID = @"cellID"; HongBaoTableViewCell *myHongBaoCell = [tableView dequeueReusableCellWithIdentifier:cellID]; if (! myHongBaoCell) { myHongBaoCell = [[HongBaoTableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; } if (showMode==0) {//展示所有的 if (allHongBao != nil && ![allHongBao isEqual:[NSNull null]] && [allHongBao count] > 0) { myHongBaoCell.RMB.text = @"¥"; myHongBaoCell.hongBaoBackImage.image = [UIImage imageNamed:allHongBao[indexPath.row][@"backImage"]]; myHongBaoCell.hongBaoJZ.text = allHongBao[indexPath.row][@"coupon_price"]; myHongBaoCell.hongBaoLaiYuan.text = allHongBao[indexPath.row][@"source"]; myHongBaoCell.guoQiShiJian.text = allHongBao[indexPath.row][@"expire_time"]; //测试下 myHongBaoCell.showMode = showMode; } return myHongBaoCell; } else if(showMode ==1)//展示可以合并的 { if (self.combineArr != nil && ![self.combineArr isEqual:[NSNull null]] && [self.combineArr count] > 0) { myHongBaoCell.RMB.text = @"¥"; myHongBaoCell.hongBaoBackImage.image = [UIImage imageNamed:self.combineArr[indexPath.row][@"backImage"]]; myHongBaoCell.hongBaoJZ.text = self.combineArr[indexPath.row][@"coupon_price"]; myHongBaoCell.hongBaoLaiYuan.text = self.combineArr[indexPath.row][@"source"]; myHongBaoCell.guoQiShiJian.text = self.combineArr[indexPath.row][@"expire_time"]; //测试下 myHongBaoCell.showMode = showMode; myHongBaoCell.isSelected = self.combineArr[indexPath.row][@"isSelected"]; } return myHongBaoCell; } return nil;}
#import <UIKit/UIKit.h>@interface HongBaoTableViewCell : UITableViewCell@property (strong,nonatomic) UIImageView *hongBaoBackImage;@property (strong,nonatomic) UILabel *RMB;@property (strong,nonatomic) UILabel *hongBaoJZ;@property (strong,nonatomic) UILabel *hongBaoLaiYuan;@property (strong,nonatomic) UILabel *guoQiShiJian;/** * 描述红包可否合并 */@property (nonatomic,strong) UILabel * combineDes;/** * 展现红包可否合并时候的提示选项,展示的是照片 */@property (nonatomic,strong) UIButton * tipimg;/** * cell的显示模式,正常模式为0,等待选择模式为1 */@property (nonatomic,assign) NSInteger showMode;/** * 描述在可以合并的状态下是不是被选中 */@property (nonatomic,assign) NSString* isSelected;@end
上面是单元格头文件的内容,暴漏出这么多的接口,在这个项目中,有些界面暴漏出属性传值达几十个,都是类似于上面那样的赋值方式,代码的复用也几乎就是赋值粘贴。我自己已经按照要求实现功能了,但是费了好大劲才看懂这没有注释的代码,而且这么简单的界面的代码也显的非常长。如此简单的一个界面,如果要增加点新功能的话,可以看出需要做很多无用功,但是如果用MVC模式呢,给红包建立一个模型,类型多的时候只要增加一个属性表示可否拆分,把红包单元格里面类的头文件暴漏出来的这些控件都放在.m文件,只在头文件声明一个红包的模型类属性,在.m文件重写这个属性,然后用模型给cell里面的UI控件赋值。在控制器里面展示红包的数组放的不再是自己定义的字典,而是模型
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
在这个方法声明一个单元格后直接给单元格的模型进行属性赋值不就完了,模型里面如果在加点注释,很快就可以看懂了。太坑了。以后不知道还会加什么功能,希望这样的活不会落在我身上。
分享这个的目的不是嘲笑,毕竟我也没有工作多长时间,但是感觉基本的编程规范还是要有的,不然你走了,你挖的坑还是让后面的人填补,而且自己的技术也不会提高吧。我感觉这样写代码的目的绝对不是出于时间紧迫吧,用MVC开发绝对比这个要快。自己最讨厌干帮别人埋坑了,你做不好上级怪你能力不行,因为他只看功能。而且他认为目前某些功能已经有了,你接着做都做不好,更是能力低下的表现,比如上面这个简单的例子。而挖坑的人呢,早就跳槽去其他地方了,薪水还涨了。以后如果有时间还要填这些坑,但是我现在不考虑了,目前的打算是只添加注释,能不改就不改,除非一个界面上所有的懂看懂了,并且有时间工作需要的时候再重写,苦逼的我还是继续奋斗吧,为梦想。
- iOS开发里不用MVC反面教材
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS里的MVC
- iOS 开发里使用MVC编程模式项目
- 移动 摇杆的创建
- Oracle 10g R2升级10.2.0.5.0步骤
- Gatling官网教程翻译之Concepts
- 线程同步与锁定_synchronized_单例模式_doubleCheckingJAVA178-179
- c#set 和get用法
- iOS开发里不用MVC反面教材
- javascript的window 与 document
- ORMLite 常用方法说明
- Unity3d获取物体的所有子物体对象(child)
- IOS中CALayer的使用
- 跟siki老师学C#第六天
- Tkinter 鼠标键盘事件
- 【慕课笔记】第四章 流程控制语句 第6节 JAVA条件语句之while
- 动态规划算法实现