您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Cocoa 框架 For iOS(二)对象的分配初始化、内省、单例
 
作者:likendsl 来源:CSDN 发布于 2015-01-30
  1871  次浏览      16
 

1、Cocoa对象的创建

我们都知道创建一个对象有两步:alloc 和 init(对象分配和初始化),两步缺一不可。初始化一般都是紧接着对象分配的后面进行,但是这两个操作的作用是完全不同的。

分配对象:就是Cocoa从应用程序的虚拟内存中为对象分配一块内存。Cocoa会根据对象的实例变量(类型和变量的排列顺序)计算内存大小并分配内存。为了分配内存,你需要向类对象(类对象上篇详细讲了它的由来和作用)发送alloc 或者allocWithZone:发送消息。消息返回一个未初始化的类实例。那发送分配消息除了分配内存外,还做了其他的一些很重要的工作:

1.对象的保持(retain)数设置为1.

2.分配的对象的isa指针指向类对象。

3.把对象所有的实例变量初始化为0.也可以理解成0的等价类型:nil NULL

这样所有的对象都有了isa指针,而且指向它们对应的类对象,这样对象就可以找到它运行时的信息。比如对象在继承层次机构上所在的位置(哪个是父类,哪个是子类等信息),它实现的协议,还有能响应什么消息。

即便如此,alloc之后的对象还不是一个可用的对象,对象必须初始化。

1.1初始化对象

初始化过程就是把对象的实例变量设置成有效合理的值,或者说你想要的数据。如果你的类没有实现初始化方法,它会调用父类的。

初始化方法的形式

初始化方法是实例方法,返回的是id类型的对象。初始化方法是讲究形式的,不能乱写。方法你可以有参数,多个也行,但是必须是init开头,比如:

- (id)initWithArray:(NSArray *)array;  (from NSSet)

参数形式:WithType:

初始化的问题

初始化也有问题?啥问题!?有时候初始化返回的并不是一个新的对象。什么时候呢? 比如:我们熟悉的单例模式的时候。还有保持对象某个属性唯一的时候。账户类的id唯一性,如果初始化一个id是已存在的id,那就要返回已存在id对应的账户对象。

这时候我们需要:

1.释放刚刚分配的对象(是不是感觉很浪费,刚分配了又要释放,都没用了呢,没办法的事情啊)

2.返回已存在的账户对象

有时候你初始化对象失败了,也需要有一些操作。怎么会失败呢?比如:initFromFile: 这个初始化方法,它是要从一个文件初始化,万一这个文件不存在,那就是初始化失败,初始化失败了怎么办:

1.释放刚刚分配的对象

2.返回nil

对象不能重复初始化,不然会产生NSInvalidArgumentException异常。

实现初始化方法

自定义类可能就需要自己写初始化的方法 了,可以有一个或多个初始化方法,看你设计的类的需要。不过实现初始化方法需要遵循以下步骤:

1.先要调用父类的初始化方法

2.检查父类初始化返回的对象,如果是nil则初始化失败,也返回nil

3.在初始化实例变量时,如果它们是其他对象的引用,必要时要进行retain和copy

4.如果返回一个已存在的对象,那首先释放新分配的对象(刚才提到的账号的类)

5.遇到问题初始化不成功(比如初始化文件失败),返回nil

6.如果没有问题,返回self。初始化完成

下面这个例子能说明这几个步骤,请看:

- (id)initWithAccountID:(NSString *)identifier {  
if ( self = [super init] ) {
Account *ac = [accountDictionary objectForKey:identifier];
if (ac) { // object with that ID already exists
[self release];
return [ac retain];
}
if (identifier) {
accountID = [identifier copy]; // accountID is instance variable
[accountDictionary setObject:self forKey:identifier];
return self;
} else {
[self release];
return nil;
}
} else
return nil;
}

注意:子类初始化时,必须先调用父类的初始化方法,以保证继承链中父类的实例变量得到正确的赋值。

下图解释继承链的初始化过程:

1.2 dealloc方法

dealloc和init方法是相呼应的。dealloc确保的是对象的实例变量和动态分配的内存被正确的释放。和init方法相反,父类的dealloc是在释放了其他的之后最后调用的。

- (void)dealloc {  
[accountDictionary release];
if ( mallocdChunk != NULL )
free(mallocdChunk);
[super dealloc];
}

1.3 工厂类方法

工厂类方法把分配对象和初始化合二为一,返回创建对象,而且还自动释放。这些方法的形式一般是:+ (type)className...

NSDate工厂类方法:

+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;  
+ (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs;
+ (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;

测试下第一个代码:

NSDate *now = [NSDate dateWithTimeIntervalSinceNow: 0];  
NSLog(@"now:%@",now);

打印出来now:2012-10-23 06:39:25 +0000

引当前时间为基准,0是当前时间,+0000表示是时区,咱们是8时区,+8是14:39。如果参数是24*60*60是明天的时间,如果是负数那就是昨天的时间。

NSData提供下面的工厂方法:

+ (id)dataWithBytes:(const void *)bytes length:(unsigned)length;  
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length
freeWhenDone:(BOOL)b;
+ (id)dataWithContentsOfFile:(NSString *)path;
+ (id)dataWithContentsOfURL:(NSURL *)url;
+ (id)dataWithContentsOfMappedFile:(NSString *)path;

举个 + (id)

dataWithContentsOfURL的例子,下载图片,毫无压力。

NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];  
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc]initWithData:data];

2、运行时内省的能力

内省(Introspection)是面向对象语言和环境的重要特性,Objective-C和Cocoa在这方面做的很好。内省是对象自己检查自己做为运行时对象详细信息的一种能力。这些详细信息包括对象在继承树上的位置,对象是否遵循特定的协议,以及是否可以响应特定的消息。NSObject协议和类定义了很多内省方法,用于查询运行时信息,以便根据对象的特征进行识别。

灵活的使用内省能力可以让你的程序更稳定强大。内省可以避免错误地进行消息派发、对象相等的错误判断等问题。下面介绍内省的一些实用方法:

2.1 定位继承关系

NSObject协议声明了几个方法,用于确定对象在类层次中的位置。class返回类的Class对象。superclass返回父类的Class对象。看下面例子:

while ( id anObject = [objectEnumerator nextObject] ) {  
if ( [self class] == [anObject superclass] ) {
// do something appropriate...
}
}

返回的两个Class对象看是否相等。

检查类对象的从属关系:isKindOfClass:判断是否是这个类的或这个类的子类的实例。isMemberOfClass: 这个更严格些,判断是否是这个类的实例。例子:

if ([item isKindOfClass:[NSData class]]) {  
const unsigned char *bytes = [item bytes];
unsigned int length = [item length];
// ...
}

2.2 判断方法的实现或者是否遵循某个协议

NSObject还有两个功能更加强大的内省方法,即respondsToSelector:和conformsToProtocol:。两个是实例方法。respondsToSelector判读对象是否实现某个的方法,conformsToProtocol判断是否遵循指定的正式协议(正是协议的意思是实现该协议的所有方法)。所有继承NSObject的类都有有这两个方法。

respondsToSelector例子:

- (void)doCommandBySelector:(SEL)aSelector {  
if ([self respondsToSelector:aSelector]) {
[self performSelector:aSelector withObject:nil];
} else {
[_client doCommandBySelector:aSelector];
}
}

2.3 对象的比较

hash和isEqual:方法都在NSObject协议中声明,且彼此关系紧密。实现hash方法会返回一个整型数。两个对象相等意味着它们有相同的哈希值。如果您的对象可能被包含在象NSSet这样的集合中,则需要定义hash方法,并确保该方法在两个对象相等的时候返回相同的哈希值。不过NSObject类中缺省的isEqual实现只是简单地检查指针是否相等。

isEqual方法例子:

- (void)saveDefaults {  
NSDictionary *prefs = [self preferences];
if (![origValues isEqual:prefs])
[Preferences savePreferencesToDefaults:prefs];
}

如果子类增加了实例变量,比较子类需要对子类的实例变量也做比较才能确定对象是否相等时,需要重载isEqual方法:

- (BOOL)isEqual:(id)other {  
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self isEqualToWidget:other];
}

- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
if (self == aWidget)
return YES;
if (![(id)[self name] isEqual:[aWidget name]])
return NO;
if (![[self data] isEqualToData:[aWidget data]])
return NO;
return YES;
}

3、对象可变性(mutable)

3.1 为什么要有可变与不可变对象

创建对象的时候,选可变的对象还是选不可变的对象呢?怎么决定呢。先看看为什么要有可变与不可变这两种对象的存在。

可变的对象的类前面都有 Mutable的关键字,这些类有:

NSMutableArray

NSMutableDictionary

NSMutableSet

NSMutableIndexSet

NSMutableCharacterSet

NSMutableData

NSMutableString

NSMutableAttributedString

NSMutableURLRequest

它们都是对应的不可变类的子类。

如果对象都是可变的,那在某些场景中是很不安全和不可靠的。比如你的某个对象当做参数传给了某个方法,你不希望你的对象被改变。这时这个方法却你的变量改变了,这是你不想要的结果。而在另外一些场景却相反。OK,为了对应不同的场景,对象就必须有可变与不可变之分了。

3.2 什么时候用可变对象

当需要在对象创建之后频繁或不断地对其内容进行修改时,请使用可变对象

有些时候,用一个不可变对象取代另一个可能更好。比如,大多数保留字符串的实例变量都应该被赋值为一个不可变的NSString对象,而这些对象则用“setter”方法来进行替换。

依靠返回类型来进行可变性提示。

如果你不能确定一个对象是可变的,则将它当成不可变的处理。

4、创建单例

创建单例的步骤:

1.声明一个单例对象的静态实例,并初始化为nil。

2.在该类的类工厂方法(名称类似于“sharedInstance”或“sharedManager”)中生成该类的一个实例,但仅当静态实例为nil的时候。

3.重载allocWithZone:方法,确保当用户试图直接(而不是通过类工厂方法)分配或初始化类的实例时,不会分配出另一个对象。

实现基本协议方法:copyWithZone:、release、retain、retainCount、和autorelease ,以保证单例的状态。

实现单例的代码例子:

static MyGizmoClass *sharedGizmoManager = nil;  

+ (MyGizmoClass*)sharedManager
{
@synchronized(self) {
if (sharedGizmoManager == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedGizmoManager;
}

+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self) {
if (sharedGizmoManager == nil) {
sharedGizmoManager = [super allocWithZone:zone];
return sharedGizmoManager; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}

- (id)copyWithZone:(NSZone *)zone
{
return self;
}

- (id)retain
{
return self;
}

- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}

- (void)release
{
//do nothing
}

- (id)autorelease
{
return self;
}
   
1871 次浏览       16
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...