M:model
V:View
C:Controller
1.M用来存取数据,所有和存取数据有关的工作交给他就好了.
下面直接用例子来说明:
#import <UIKit/UIKit.h>
#import "Student.h"
@interface Student : NSObject
@property (nonatomic,retain)NSString *name;
@property (nonatomic,retain)NSString *sex;
@property (nonatomic,retain)NSString *phone;
@property (nonatomic,retain)NSString *photo;
@property (nonatomic,retain)NSString *hobby;
@end
上面定义了若干的属性,如果是一个新手的话,会在.m中写很多的初始化方法,便利构造器等,用来初始化或给属性赋值:
- (instancetype)initWithName:(NSString *)name sex:(NSString *)sex ....
+ (Student *)studentWithName:(NSString *)name sex:(NSString *)sex ....
或者你学的还不错,你会这样写:
- (instancetype)initWithDictionary:(NSDictionary *)dic;
将student的属性以键值对的方式放在一个字典里,然后把字典传进来,在方法内部进行赋值.
相比之下这个方法就要比上边的初始化要好得多了(字典的好处就在于,可以将字典内容存储到plist文件当中,实现类似存档读档的功能)
操作方便,还节省时间,是个不错的方法
but...这些都不是我要说的,来看看真正的大神怎么写吧:
如果是我的话(哈哈纯属娱乐~~),我会使用KVC模式(注意,不是KFC哦).只需要给Model类添加需要的属性就好了,也就是说,用到什么数据,就定义为属性.不需要写初始化方法,只要用KVC就ok了,这样代码简单占资源少.(KVC:每个类的实例都会有KVC的相关方法,创建并初始化一个类的实例,就能用实例调用KVC的方法了,如setValue: forKey:之类的方法.KVC方法是间接赋值,和属性的setter不一样,setter是直接赋值.一个类没有setter方法也能用KVC赋值),如:
#import "Student.h"
@implementation Student
-(void)dealloc
{
[_name release];
[_sex release];
[_phone release];
[_photo release];
[_hobby release];
[super dealloc];
}
//不重写这个函数的话,如果k和v不匹配会崩溃,比如传进来的key有四个而类里有三个key;
//默认的实现是抛出一个异常
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}
@end
.m文件中没有定义初始化方法
Student *stu=[[Student alloc] init];
[stu setValuesForKeysWithDictionary:[students objectAtIndex:i]];
如题:student类中没有定义初始化方法,但是用KVC可以对齐进行赋值,省下了很多代码,而且还很简单,高手都这么干,嗯
2.V只用来做和显示有关的事情.是什么数据由你Model决定,怎么显示就是我的事了
一般情况下C在控制V的时候,都会用M对其进行传值.作为一个V,你所需要做的就是让外部使用你的时候尽可能的简单,也就是说,你要尽量将一些复杂重复的代码封装在你的内部,对外提供一个简单的接口,这样,外界使用者不用知道你具体是怎么实现的,只要传入一个参数(数组,字典等)就能完成任务.例如:
//cell的声明(.h)
@interface StudentCell : UITableViewCell
@property (nonatomic,retain)UILabel *nameLaber;//显示姓名的laber,下同
@property (nonatomic,retain)UILabel *sexLaber;
@property (nonatomic,retain)UILabel *phoneLaber;
@property (nonatomic,retain)UIImageView *photoImage;//显示头像
@property (nonatomic,retain)Student *stu;
@end
//cell的实现(.m)
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//初始化相关属性
self.photoImage=[[[UIImageView alloc] initWithFrame:CGRectMake(5, 2, 95, 96)] autorelease];
self.nameLaber=[[[UILabel alloc] initWithFrame:CGRectMake(100, 2, 200, 30)] autorelease];
self.sexLaber=[[[UILabel alloc] initWithFrame:CGRectMake(100, 34, 200, 30)] autorelease];
self.phoneLaber=[[[UILabel alloc] initWithFrame:CGRectMake(100, 68, 200, 30)] autorelease];
_phoneLaber.numberOfLines=0;
_phoneLaber.font=[UIFont systemFontOfSize:18];
_photoImage.contentMode=UIViewContentModeScaleAspectFit;
[self.contentView addSubview:_photoImage];
[self.contentView addSubview:_nameLaber];
[self.contentView addSubview:_sexLaber];
[self.contentView addSubview:_phoneLaber];
}
return self;
}
//tableView中创建cell
static NSString *cellIdentifier=@"cell";
Student *student=[_studentsArray objectAtIndex:indexPath.row];
cellIdentifier=student.sex;
StudentCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil) {
cell=[[[StudentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
/*
在这里,你(C)要给创建好的cell(V)传值(M),告诉V,你要他显示什么内容,所以你需要给cell里声明的作为cell的subView的属性赋值.
一个新手的做法是,直接给每个View的子类赋值.
如:
cell.nameLaber.text=student.name;
cell.sexLaber.text=student.sex;
...
...
有一个附一个,有100个呢?这样是很麻烦的对吧,你创建一个cell,就要写一次.太浪费时间&&空间了.可是,又不能在cell的初始化方法里写,因为那时候你还不知道需要的值是什么.
*/
/*
****************************标记
cell.stu=[_studentsArray objectAtIndex:indexPath.row];
*/
return cell;
上面是关于tableView的一段代码,当你创建完毕一个cell,你要对其你行传值,重复的写一段相似的代码显然不理智.下面看看高手会怎么做:
如果我们在cell(View)里定义一个属性,类型为Student(Model),用来存放外界(Controller)传进的值,而属性的setter方法我们按下面这样重写
- (void)setStu:(Student *)stu
{
self.nameLaber.text=stu.name;
self.sexLaber.text=stu.sex;
CGRect rect=[stu.phone boundingRectWithSize:CGSizeMake(200, 5000) options:NSStringDrawingUsesLineFragmentOrigin attributes:[NSDictionary dictionaryWithObjectsAndKeys:self.phoneLaber.font,NSFontAttributeName, nil] context:nil];
self.phoneLaber.frame=CGRectMake(100, 68, 200, rect.size.height);
self.phoneLaber.text=stu.phone;
self.photoImage.image=[UIImage imageNamed:stu.photo];
}
这样,在上面传值的部分,我们就可以用标记部分所写的方法了,就一行代码,简单吧~
下面我们看看这段代码是怎么工作的
cell.stu就相当于调用了setter方法,我们将整个stu传进来,而不是在外边一个一个的传进来.
在内部,我们再对相关属性进行赋值,怎么样,是不是很简便?
(其实,许多系统内部的类,都用了这个方法,比如UIWindou的setRootViewController方法,实际上就在内部调用了[self addSubview:rootViewController.view]方法)
3.C用来做和控制有关的一切事情,目的是为了去耦.
试想,如果一个View中的所有姿势图的事件都由他自己去实现,那么这个类就相当于废掉了,因为你的类只能在你自己的这个项目里用,换到别的地方就不行了.
面向对象的思想是什么?提高代码的复用性.目标是什么?高内聚低耦合.
所谓高内聚,就是指,相同的功能实现代码要紧密的结合(在党的周围0.0哈哈...)
低耦合,就是说,你的类要尽可能的对你的其他类没有依赖性,也就是说,你的类的使用,不依赖于其他的类.
举个例子,你实现了一个你自己定义的UIView的子类,但是你的方法都写在了类的内部,你的View中的button的title是登陆,不能改.button的点击事件固定为跳转到你定义好的另一个界面(比如京东的商品界面).那么下次如果你要做一个淘宝的软件,这个登陆界面就不能复用了,因为你的这个界面的button只能跳转到京东的商品界面,所以你还要重写一个登陆界面.
如果此时你把button的事件处理交给一个Controller,让他去实现事件处理,让他去决定点击之后你需要做什么,这就好办了.下次我写别的程序的时候,我就可以把决定权交给那个Controller了.这样就实现了代码的复用.
有关去耦的设计模式有:target...action设计模式,代理设计模式,适配器模式等,有兴趣的朋友可以自行查阅相关资料.
--The end