Thu, 17 Apr 2008 02:06:36 +0800
无处不在的
MVC
在传统的开发模式中,我们很容易陷入“ 胶水代码” 的陷阱里。所谓的“ 胶水代码” ,顾名思义,就是仅仅用来保持用户界面数据、状态同步的函数调用的集合体。这些函数调用扯不断,理还乱,并且使代码变的非常冗长、易出错、不易维护。
为了解决这个问题, Cocoa提供了多个内部机制: Key-Value Coding( KVC)、 Key-Value Observing( KVO)、 Key-Value Binding( KVB)。这些机制通过规定了一组通用的 Cocoa命名法则、调用规则等,实现了如下功能:
1. 使用一对高度规范化的访问方法,获取以及设置任何对象的任何属性的值(所谓的属性既可以是个实实在在的成员变量,也可以是通过一对成员方法所抽象出的该对象的一个性质)。
2. 通过继承一个特定的方法,并且指定希望监视的对象及希望监视的属性名称,就能在该对象的指定属性的值发生改变时,得到一个“ 通知” (尽管这不是一个真正意义上的通知),并且得到相关属性的值的变化(原先的值和改变后的新值)。
3. 通过一个简单的函数调用,使一个视图对象的一个指定属性随时随地都和一个控制器对象或模型对象的一个指定属性保持同步。
一个开发者可以利用这些功能,将自己创建的类写的很范化、很通用,然后通过KVB 将多个视图“ 绑定” 到一个或多个控制器上,再将控制器绑定到最底层的数据模型上。这样一来,任何一个视图上的改变都会通过KVC 被“ 压” 到控制器里,然后又通过KVC 从控制器“ 压” 到数据模型里。当数据模型中的值发生改变时,一个或多个控制器又会得到 KVO 的“ 通知” ,接着只要被绑定了的视图又会得到这一个或多个控制器的 KVO“ 通知” 。这样以来,开发者只需要在适当的时候告诉 Cocoa ,什么对象的什么值该和什么对象的什么值绑定,就可以了,其余数据更新、格式化等工作 Cocoa 都会替你完成。
是不是很方便呢?
Easy Life之一:内存管理
内存管理是令很多开发人员头大的问题,在 Cocoa 中,内存管理是通过引用计数器模型完成的。
Cocoa 中的每个对象都拥有一个引用计数器,用来维持自己的生命周期。每当一个对象需要“ 使用” 或“ 占有” 另一个对象的时候,它通过向该对象发送一个retain 消息来对该对象的引用计数器进行自增,而当它不再需要(或使用完)该对象的时候,它通过向该对象发送一个release 消息来对该对象的引用计数器进行自减。当一个对象的引用计数器自减到零时,该对象就会被释放。
下面我们来看一个例子,例如:
NSString *aString = [[NSString alloc] initWithString:@"This is a demo."];
这段代码会创建一个NSString 对象,并对其进行初始化。当一个对象被创建的时候,它的引用计数器会被设为1 。因此当您不再需要该对象,只要直接对其发送release 消息,它就会被直接析构。当您有别的代码块也需要使用这个NSString 时,您可以对这个NSString 对象调用一次retain 来增加它的引用计数器:
[aString retain]; 这个时候,它的引用计数器值就是2 了。当您使用完该对象时,如果您对该对象调用过retain ,那就应该“ 对称” 地调用一次release 。这时一种基本地编程规范,我将它称为“ 谁retain ,谁release” 。
[aString release];
调用完以后,引用计数器再次回到1 。最后,当我们彻底不需要这个对象的时候,我们可以这么做:
[aString release];
aString = nil;
上两行代码中,第一句会负责将这个NSString 对象析构,第二句会负责将原来指向这个NSString 对象的指针(NSString *aString )“ 归零” ,因为“ 野指针” 随时可能造成您程序的异常及崩溃。
听着是不是挺简单?
当然也有稍微复杂一些的情况,话说一开始我们有提到一个叫作NSAutoreleasePool 的类吧?NSAutoreleasePool 是 Cocoa 内存管理机制里很重要的一个环节。我们在本着“ 谁retain ,谁release” 的对象使用的大前提下,经常会碰到这么一个问题,那就是我们希望返回一个在局部中创建的对象:
- (NSString)demoString
{
NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
return result;
}
在“ 谁retain ,谁release” 的原则下,上面的代码显然只负责了retain (alloc 调用等效于retain ),但是没有负责release ,因此这么写可能会造成内存泄露,因为调用这个方法(或这个API )的代码段并不知道究竟是否需要负责释放这个方法(或这个API )的返回值。
但是如果我们将它直接release 了:
- (NSString)demoString
{
NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
[result release];
return result;
}
那return 的将会是个“ 野指针” (或者如果你干的足够干净,return 的是个零指针),不是我们需要的值。因此我们需要一个能够延迟释放,并且能够自动释放的机制。于是,人们发明了名叫NSAutoreleasePool 的又一个轮子,而代码则变成了这个样子:
- (NSString)demoString
{
NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
[result autorelease];
return result;
}
在对一个对象发送了 autorelease之后,这个对象不会被立即释放,而是被 “登记 ”到了离它最近的一个 NSAutoreleasePool对象上。当该 NSAutoreleasePool被清空或释放的时候,这个 “登记 ”了的对象才会被真正发送一个 release消息。
Easy Life之二:容器
容器是让多数程序员又爱又恨的东西。在 Cocoa 中,容器是如此的简单易用以至于您一旦用过,就会对它们“ 爱不释手” 。 Cocoa 中的容器类主要有这么几个:NSString 、NSArray 、NSDictionary 、NSSet 和NSIndexSet 等,它们都是Foundation Framework 的一部分。
为什么人们会对 Cocoa 的容器 “ 爱不释手 ” 呢?
原因一: NSArray、 NSDictionary、 NSSet都不强制其内部元素类型的一致性。举个简单的例子:
NSString *aString = [NSString stringWithString:@"This is a demo."];
NSNumber *aNumber = [NSNumber numberWithInteger:0];
NSArray *anArray = [[NSArray alloc] initWithObjects:aString, aNumber, nil];
在上述例子中,我们首先建立了一个NSString 对象,然后又建立了一个NSNumber 对象,最后我们将这两个NSString 和NSNumber 对象都“ 塞” 到了一个NSArray 对象中。
有够爽吧?连想都别想,什么东西都能往里面装(基本类型、结构体除外)!
原因二:容器类的 “可修改 ”和 “不可修改 ”
上面我们展示的NSString 、NSArray 、NSDictionary 、NSSet 以及NSIndexSet 等,都是容器的“ 不可修改” 的版本。所谓的“ 不可修改” ,指的是这个容器一旦被创建以后,我们就不可以通过代码修改它的集合。那如果我们需要修改(例如添加、删除、替换)这些容器的元素,该怎么办呢?
Cocoa中几乎所有的容器类,都提供了另外一个 “可修改 ”的版本。例如:继承自 NSString的 NSMutableString、继承自 NSArray的 NSMutableArray、继承自 NSDictionary的 NSMutableDictioanry、继承自 NSSet的 NSMutableSet以及继承自 NSIndexSet的 NSMutableIndexSet等。这些 “可修改 ”的版本提供了简单直观的方法,用来修改其内部的元素。例如:
NSString *aString = [NSString stringWithString:@"This is a demo."];
NSNumber *aNumber = [NSNumber numberWithInteger:0];
NSMutableArray *aMutableArray = [NSMutableArray array];
[aMutableArray addObject:aString];
[aMutableArray addObject:aNumber];
[aMutableArray removeObjectAtIndex:0];
[aMutableArray removeAllObjects];
在上述代码中,第一、二行建立了一个NSString 对象和一个NSNumber 对象。第三行建立了一个NSMutableArray 对象(也就是一个“ 可修改” 的NSArray 对象)。第四、五行通过-addObject: 方法分别将第一、二行建立的NSString 对象和NSNumber 对象加入了这个“ 可修改” 的NSArray 里。第六行则是根据我们给定的索引号0 ,删除了数组中的第一个元素。第七行的-removeAllObjects
在传统的开发模式中,我们很容易陷入“ 胶水代码” 的陷阱里。所谓的“ 胶水代码” ,顾名思义,就是仅仅用来保持用户界面数据、状态同步的函数调用的集合体。这些函数调用扯不断,理还乱,并且使代码变的非常冗长、易出错、不易维护。
为了解决这个问题, Cocoa提供了多个内部机制: Key-Value Coding( KVC)、 Key-Value Observing( KVO)、 Key-Value Binding( KVB)。这些机制通过规定了一组通用的 Cocoa命名法则、调用规则等,实现了如下功能:
1. 使用一对高度规范化的访问方法,获取以及设置任何对象的任何属性的值(所谓的属性既可以是个实实在在的成员变量,也可以是通过一对成员方法所抽象出的该对象的一个性质)。
2. 通过继承一个特定的方法,并且指定希望监视的对象及希望监视的属性名称,就能在该对象的指定属性的值发生改变时,得到一个“ 通知” (尽管这不是一个真正意义上的通知),并且得到相关属性的值的变化(原先的值和改变后的新值)。
3. 通过一个简单的函数调用,使一个视图对象的一个指定属性随时随地都和一个控制器对象或模型对象的一个指定属性保持同步。
一个开发者可以利用这些功能,将自己创建的类写的很范化、很通用,然后通过KVB 将多个视图“ 绑定” 到一个或多个控制器上,再将控制器绑定到最底层的数据模型上。这样一来,任何一个视图上的改变都会通过KVC 被“ 压” 到控制器里,然后又通过KVC 从控制器“ 压” 到数据模型里。当数据模型中的值发生改变时,一个或多个控制器又会得到 KVO 的“ 通知” ,接着只要被绑定了的视图又会得到这一个或多个控制器的 KVO“ 通知” 。这样以来,开发者只需要在适当的时候告诉 Cocoa ,什么对象的什么值该和什么对象的什么值绑定,就可以了,其余数据更新、格式化等工作 Cocoa 都会替你完成。
是不是很方便呢?
Easy Life之一:内存管理
内存管理是令很多开发人员头大的问题,在 Cocoa 中,内存管理是通过引用计数器模型完成的。
Cocoa 中的每个对象都拥有一个引用计数器,用来维持自己的生命周期。每当一个对象需要“ 使用” 或“ 占有” 另一个对象的时候,它通过向该对象发送一个retain 消息来对该对象的引用计数器进行自增,而当它不再需要(或使用完)该对象的时候,它通过向该对象发送一个release 消息来对该对象的引用计数器进行自减。当一个对象的引用计数器自减到零时,该对象就会被释放。
下面我们来看一个例子,例如:
NSString *aString = [[NSString alloc] initWithString:@"This is a demo."];
这段代码会创建一个NSString 对象,并对其进行初始化。当一个对象被创建的时候,它的引用计数器会被设为1 。因此当您不再需要该对象,只要直接对其发送release 消息,它就会被直接析构。当您有别的代码块也需要使用这个NSString 时,您可以对这个NSString 对象调用一次retain 来增加它的引用计数器:
[aString retain]; 这个时候,它的引用计数器值就是2 了。当您使用完该对象时,如果您对该对象调用过retain ,那就应该“ 对称” 地调用一次release 。这时一种基本地编程规范,我将它称为“ 谁retain ,谁release” 。
[aString release];
调用完以后,引用计数器再次回到1 。最后,当我们彻底不需要这个对象的时候,我们可以这么做:
[aString release];
aString = nil;
上两行代码中,第一句会负责将这个NSString 对象析构,第二句会负责将原来指向这个NSString 对象的指针(NSString *aString )“ 归零” ,因为“ 野指针” 随时可能造成您程序的异常及崩溃。
听着是不是挺简单?
当然也有稍微复杂一些的情况,话说一开始我们有提到一个叫作NSAutoreleasePool 的类吧?NSAutoreleasePool 是 Cocoa 内存管理机制里很重要的一个环节。我们在本着“ 谁retain ,谁release” 的对象使用的大前提下,经常会碰到这么一个问题,那就是我们希望返回一个在局部中创建的对象:
- (NSString)demoString
{
NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
return result;
}
在“ 谁retain ,谁release” 的原则下,上面的代码显然只负责了retain (alloc 调用等效于retain ),但是没有负责release ,因此这么写可能会造成内存泄露,因为调用这个方法(或这个API )的代码段并不知道究竟是否需要负责释放这个方法(或这个API )的返回值。
但是如果我们将它直接release 了:
- (NSString)demoString
{
NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
[result release];
return result;
}
那return 的将会是个“ 野指针” (或者如果你干的足够干净,return 的是个零指针),不是我们需要的值。因此我们需要一个能够延迟释放,并且能够自动释放的机制。于是,人们发明了名叫NSAutoreleasePool 的又一个轮子,而代码则变成了这个样子:
- (NSString)demoString
{
NSString *result = [[NSString alloc] initWithString:@"This is a demo."];
[result autorelease];
return result;
}
在对一个对象发送了 autorelease之后,这个对象不会被立即释放,而是被 “登记 ”到了离它最近的一个 NSAutoreleasePool对象上。当该 NSAutoreleasePool被清空或释放的时候,这个 “登记 ”了的对象才会被真正发送一个 release消息。
Easy Life之二:容器
容器是让多数程序员又爱又恨的东西。在 Cocoa 中,容器是如此的简单易用以至于您一旦用过,就会对它们“ 爱不释手” 。 Cocoa 中的容器类主要有这么几个:NSString 、NSArray 、NSDictionary 、NSSet 和NSIndexSet 等,它们都是Foundation Framework 的一部分。
为什么人们会对 Cocoa 的容器 “ 爱不释手 ” 呢?
原因一: NSArray、 NSDictionary、 NSSet都不强制其内部元素类型的一致性。举个简单的例子:
NSString *aString = [NSString stringWithString:@"This is a demo."];
NSNumber *aNumber = [NSNumber numberWithInteger:0];
NSArray *anArray = [[NSArray alloc] initWithObjects:aString, aNumber, nil];
在上述例子中,我们首先建立了一个NSString 对象,然后又建立了一个NSNumber 对象,最后我们将这两个NSString 和NSNumber 对象都“ 塞” 到了一个NSArray 对象中。
有够爽吧?连想都别想,什么东西都能往里面装(基本类型、结构体除外)!
原因二:容器类的 “可修改 ”和 “不可修改 ”
上面我们展示的NSString 、NSArray 、NSDictionary 、NSSet 以及NSIndexSet 等,都是容器的“ 不可修改” 的版本。所谓的“ 不可修改” ,指的是这个容器一旦被创建以后,我们就不可以通过代码修改它的集合。那如果我们需要修改(例如添加、删除、替换)这些容器的元素,该怎么办呢?
Cocoa中几乎所有的容器类,都提供了另外一个 “可修改 ”的版本。例如:继承自 NSString的 NSMutableString、继承自 NSArray的 NSMutableArray、继承自 NSDictionary的 NSMutableDictioanry、继承自 NSSet的 NSMutableSet以及继承自 NSIndexSet的 NSMutableIndexSet等。这些 “可修改 ”的版本提供了简单直观的方法,用来修改其内部的元素。例如:
NSString *aString = [NSString stringWithString:@"This is a demo."];
NSNumber *aNumber = [NSNumber numberWithInteger:0];
NSMutableArray *aMutableArray = [NSMutableArray array];
[aMutableArray addObject:aString];
[aMutableArray addObject:aNumber];
[aMutableArray removeObjectAtIndex:0];
[aMutableArray removeAllObjects];
在上述代码中,第一、二行建立了一个NSString 对象和一个NSNumber 对象。第三行建立了一个NSMutableArray 对象(也就是一个“ 可修改” 的NSArray 对象)。第四、五行通过-addObject: 方法分别将第一、二行建立的NSString 对象和NSNumber 对象加入了这个“ 可修改” 的NSArray 里。第六行则是根据我们给定的索引号0 ,删除了数组中的第一个元素。第七行的-removeAllObjects