从 C++ 到 Objective-C(10):实例化(续)
来源:互联网 发布:在线查看网页源码 编辑:程序博客网 时间:2024/05/17 01:27
作者: DevBean 日期: 2011 年 03 月 24 日
self = [super init...]
在上一篇提到的代码中,最不可思议的可能就是这句 self = [super init...]。回想一下,self 是每个方法的一个隐藏参数,指向当前对象。因此,这是一个局部变量。那么,为什么我们要改变一个局部变量的值呢?事实上,self 必须要改变。我们将在下面解释为什么要这样做。
[super init] 实际上返回不同于当前对象的另外一个对象。单例模式就是这样一种情况。然而, 有一个 API 可以用一个对象替换新分配的对象。Core Data(Apple 提供的 Cocoa 里面的一个 API)就是用了这种 API,对实例数据做一些特殊的操作,从而让这些数据能够和数据库的字段关联起来。当继承 NSManagedObject 类的时候,就需要仔细对待这种替换。在这种情形下,self 就要指向两个对象:一个是 alloc 返回的对象,一个是 [super init] 返回的对象。修改 self 的值对代码有一定的影响:每次访问实例数据的时候都是隐式的。正如下面的代码所示:
@interface B : A{int i;} @end @implementation B -(id) init{ // 此时,self 指向 alloc 返回的值 // 假设 A 进行了替换操作,返回一个不同的 self id newSelf = [super init]; NSLog(@"%d", i); // 输出 self->i 的值 self = newSelf; // 有人会认为 i 没有变化 NSLog(@"%d", i); // 事实上,此时的 self->i, 实际是 newSelf->i, // 和之前的值可能不一样了 return self;} @end...B* b = [[B alloc] init];
self = [super init] 简洁明了,也不必担心以后会引入 bug。然而,我们应该注意旧的 self 指向的对象的命运:它必须被释放。第一规则很简单:谁替换 self 指针,谁就要负责处理旧的 self 指针。在这里,也就是 [super init] 负责完成这一操作。例如,如果你创建 NSManagedObject 子类(这个类会执行替换操作),你就不必担心旧的 self 指针。事实上,NSManagedObject 的开发者必须考虑这种处理。因此,如果你要创建一个执行替换操作的类,你必须知道如何在初始化过程中释放旧有对象。这种操作同错误处理很类似:如果因为非法参数、不可访问的资源造成构造失败,我们要如何处理?
初始化错误
初始化出错可能发生在三个地方:
- 调用 [super init...] 之前:如果构造函数参数非法,那么初始化应该立即停止;
- 调用 [super init...] 期间:如果父类调用失败,那么当前的初始化操作也应该停止;
- 调用 [super init...] 之后:例如资源分配失败等。
在上面每一种情形中,只要失败,就应该返回 nil;相应的处理应该由发生错误的对象去完成。这里,我们主要关心的是1, 3情况。要释放当前对象,我们调用 [self release] 即可。
在调用 dealloc 之后,对象的析构才算完成。因此,dealloc 的实现必须同初始化方法兼容。事实上,alloc 将所有的实例数据初始化成 0 是相当有用的。
@interface A : NSObject { unsigned int n;} -(id) initWithN:(unsigned int)value;@end @implementation A -(id) initWithN:(unsigned int)value{ // 第一种情况:参数合法吗? if (value == 0) // 我们需要一个正值 { [self release]; return nil; } // 第二种情况:父类调用成功吗? if (!(self = [super init])) // 即是 self 被替换,它也是父类 return nil; // 错误发生时,谁负责释放 self? // 第三种情况:初始化能够完成吗? n = (int)log(value); void* p = malloc(n); // 尝试分配资源 if (!p) // 如果分配失败,我们希望发生错误 { [self release]; return nil; }}@end
将构造过程合并为 alloc+init
有时候,alloc 和 init 被分割成两个部分显得很罗嗦。幸运的是,我们也可以将其合并在一起。这主要牵扯到 Objective-C 的内存管理机制。简单来说,作为一个构造函数,它的名字必须以类名开头,其行为类似 init,但要自己实现 alloc。然而,这个对象需要注册到 autorelease 池中,除非发送 retain 消息,否则其生命周期是有限制的。以下即是示例代码:
// 啰嗦的写法NSNumber* tmp1 = [[NSNumber alloc] initWithFloat:0.0f];...[tmp1 release];// 简洁一些NSNumber* tmp2 = [NSNumber numberWithFloat:0.0f];...// 无需调用 release
本文来自 DevBean's World:http://www.devbean.info。
转载时请标明文章原始出处:http://www.devbean.info/2011/03/from_cpp_to_objc_10/。
- 从 C++ 到 Objective-C(9):实例化
- 从 C++ 到 Objective-C(10):实例化(续)
- 从 C++ 到 Objective-C 之实例化
- 从 C++ 到 Objective-C(8):继承(续)
- 从C/C++到Objective-C(一)
- 从C/C++到Objective-C(二)--- 面向对象
- 从C/C++到Objective-C(三)--- 内存管理
- 从 C++ 到 Objective-C(1):前言
- 从 C++ 到 Objective-C(2):语法概述
- 从 C++ 到 Objective-C(3):类和对象
- 从 C++ 到 Objective-C(7):继承
- 从C++到objective-c----委托(模板方法)
- 从 C++到 Objective-C(2):语法概述
- 从 C++到 Objective-C(3):类和对象
- 从C++到objective-c----委托(模板方法)
- 从c++到Objective-C
- 从C++到Objective-C
- 从 C++ 到Objective-C
- undefined symbol: zend_parse_parameters
- Win32 API讲座--窗口函数(转)
- Windows消息
- Cocoa基本原理指南之三 - 根类
- 如何改变Android Progressbar默认颜色
- 从 C++ 到 Objective-C(10):实例化(续)
- java集合类(三)Arraylist实例
- Hadoop Streaming cacheFile 和cacheArchive选项
- 备忘
- MySQL事务的隔离级别和日志记录模式选择
- java集合类(二)Collections类内元素排序
- OK6410裸机程序---hello world
- 怎样用sql列出一个表的所有字段或某一字段
- java集合类(五)HashSet与TreeSet应用实例