0
点赞
收藏
分享

微信扫一扫

Cocoa 框架 For iOS(二)对象的分配初始化、内省、单例


接上一篇  ​​Cocoa 框架总结For iOS(一)​​  ,继续总结Cocoa对象

1、Cocoa对象的创建



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



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



  • 对象的保持(retain)数设置为1.
  • 分配的对象的isa指针指向类对象。
  • 把对象所有的实例变量初始化为0.也可以理解成0的等价类型:nil  NULL

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



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



1.1初始化对象



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



初始化方法的形式



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



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



参数形式:WithType: 



初始化的问题



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



这时候我们需要:



  • 释放刚刚分配的对象(是不是感觉很浪费,刚分配了又要释放,都没用了呢,没办法的事情啊)
  • 返回已存在的账户对象

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



  • 释放刚刚分配的对象
  • 返回nil



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



 



实现初始化方法



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



  1. 先要调用父类的初始化方法
  2. 检查父类初始化返回的对象,如果是nil则初始化失败,也返回nil
  3. 在初始化实例变量时,如果它们是其他对象的引用,必要时要进行retain和copy
  4. 如果返回一个已存在的对象,那首先释放新分配的对象(刚才提到的账号的类)
  5. 遇到问题初始化不成功(比如初始化文件失败),返回nil
  6. 如果没有问题,返回self。初始化完成



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



[cpp]  ​​view plain​​ ​​copy​​



  1. - (id)initWithAccountID:(NSString *)identifier {  
  2. if ( self = [super init] ) {  
  3.         Account *ac = [accountDictionary objectForKey:identifier];  
  4. if (ac) { // object with that ID already exists  
  5.             [self release];  
  6. return [ac retain];  
  7.         }   
  8. if (identifier) {  
  9. // accountID is instance variable  
  10.             [accountDictionary setObject:self forKey:identifier];  
  11. return self;  
  12. else {  
  13.             [self release];  
  14. return nil;  
  15.         }  
  16. else  
  17. return nil;  
  18. }  



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

 

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



 




Cocoa 框架 For iOS(二)对象的分配初始化、内省、单例_移动开发


 


 


1.2 dealloc方法


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


[cpp]  ​​view plain​​ ​​copy​​


  1. - (void)dealloc {  
  2.     [accountDictionary release];  
  3. if ( mallocdChunk != NULL )  
  4.         free(mallocdChunk);  
  5.     [super dealloc];  
  6. }  


1.3 工厂类方法


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


NSDate工厂类方法:


[cpp]  ​​view plain​​ ​​copy​​


  1. + (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;  
  2. + (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs;  
  3. + (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;  


测试下第一个代码:

 


[cpp]  ​​view plain​​ ​​copy​​


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


 

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

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



[cpp]  ​​view plain​​ ​​copy​​


  1. + (id)dataWithBytes:(const void *)bytes length:(unsigned)length;  
  2. + (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length;  
  3. + (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length  
  4. BOOL)b;  
  5. + (id)dataWithContentsOfFile:(NSString *)path;  
  6. + (id)dataWithContentsOfURL:(NSURL *)url;  
  7. + (id)dataWithContentsOfMappedFile:(NSString *)path;  


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


[cpp]  ​​view plain​​ ​​copy​​


  1. NSURL * url = [NSURL URLWithStri
  2. NSData * data = [NSData dataWithContentsOfURL:url];  
  3. UIImage *image = [[UIImage alloc]initWithData:data];  


2、运行时内省的能力


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

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


2.1 定位继承关系


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


[cpp]  ​​view plain​​ ​​copy​​


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

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

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

[cpp]  ​​view plain​​ ​​copy​​


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


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


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


respondsToSelector例子:


[cpp]  ​​view plain​​ ​​copy​​


  1. - (void)doCommandBySelector:(SEL)aSelector {  
  2. if ([self respondsToSelector:aSelector]) {  
  3.         [self performSelector:aSelector withObject:nil];  
  4. else {  
  5.         [_client doCommandBySelector:aSelector];  
  6.     }  
  7. }  


 

2.3 对象的比较

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

isEqual方法例子:


[cpp]  ​​view plain​​ ​​copy​​


  1. - (void)saveDefaults {  
  2.     NSDictionary *prefs = [self preferences];  
  3. if (![origValues isEqual:prefs])   
  4.         [Preferences savePreferencesToDefaults:prefs];  
  5. }  


[cpp]  ​​view plain​​ ​​copy​​


  1. - (BOOL)isEqual:(id)other {  
  2. if (other == self)   
  3. return YES;  
  4. if (!other || ![other isKindOfClass:[self class]])   
  5. return NO;  
  6. return [self isEqualToWidget:other];  
  7. }  
  8.    
  9. - (BOOL)isEqualToWidget:(MyWidget *)aWidget {  
  10. if (self == aWidget)   
  11. return YES;  
  12. if (![(id)[self name] isEqual:[aWidget name]])  
  13. return NO;  
  14. if (![[self data] isEqualToData:[aWidget data]])  
  15. return NO;  
  16. return YES;  
  17. }  


3、对象可变性(mutable)


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


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


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


  • NSMutableArray
  • NSMutableDictionary
  • NSMutableSet
  • NSMutableIndexSet
  • NSMutableCharacterSet
  • NSMutableData
  • NSMutableString
  • NSMutableAttributedString
  • NSMutableURLRequest


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


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


3.2 什么时候用可变对象


当需要在对象创建之后频繁或不断地对其内容进行修改时,请使用可变对象
有些时候,用一个不可变对象取代另一个可能更好。比如,大多数保留字符串的实例变量都应该被赋值为一个不可变的NSString对象,而这些对象则用“setter”方法来进行替换。
依靠返回类型来进行可变性提示。
如果你不能确定一个对象是可变的,则将它当成不可变的处理。

4、创建单例


创建单例的步骤:


  • 声明一个单例对象的静态实例,并初始化为nil。
  • 在该类的类工厂方法(名称类似于“sharedInstance”或“sharedManager”)中生成该类的一个实例,但仅当静态实例为nil的时候。
  • 重载allocWithZone:方法,确保当用户试图直接(而不是通过类工厂方法)分配或初始化类的实例时,不会分配出另一个对象。
  • 实现基本协议方法:copyWithZone:、release、retain、retainCount、和autorelease ,以保证单例的状态。


实现单例的代码例子:


[cpp]  ​​view plain​​ ​​copy​​


  1. static MyGizmoClass *sharedGizmoManager = nil;  
  2.    
  3. + (MyGizmoClass*)sharedManager  
  4. {  
  5.     @synchronized(self) {  
  6. if (sharedGizmoManager == nil) {  
  7. // assignment not done here  
  8.         }  
  9.     }  
  10. return sharedGizmoManager;  
  11. }  
  12.    
  13. + (id)allocWithZone:(NSZone *)zone  
  14. {  
  15.     @synchronized(self) {  
  16. if (sharedGizmoManager == nil) {  
  17.             sharedGizmoManager = [super allocWithZone:zone];  
  18. return sharedGizmoManager;  // assignment and return on first allocation  
  19.         }  
  20.     }  
  21. return nil; //on subsequent allocation attempts return nil  
  22. }  
  23.    
  24. - (id)copyWithZone:(NSZone *)zone  
  25. {  
  26. return self;  
  27. }  
  28.    
  29. - (id)retain  
  30. {  
  31. return self;  
  32. }  
  33.    
  34. - (unsigned)retainCount  
  35. {  
  36. return UINT_MAX;  //denotes an object that cannot be released  
  37. }  
  38.    
  39. - (void)release  
  40. {  
  41. //do nothing  
  42. }  
  43.    
  44. - (id)autorelease  
  45. {  
  46. return self;  
  47. }  



举报

相关推荐

0 条评论