Java是一门面向对象的高级语言,但也保留了基本数据类型和运算符这些面向过程的元素。如何平衡对象和简单数据类型之间的抉择,被认为是设计面向对象语言所面临的挑战。Java很出色的完成了这个挑战。
Java中有八种基本数据类型,见下表:
数值类型 byte short char int long float double boolean
长度(字节) 1 2 2 4 8 4 8 1
包装器 Byte Short Character Integer Long Float Double Boolean
短字节类型向长字节类型转换,大多为无损转换。需要说明的有以下几点:
Java数据类型不存在unsigned;
byte、short、char的单目数学运算的返回结果自动转型为int类型,即byte b = 0; b+1的结果为int类型,而b++结果为byte类型,b += 3也是合法的语句;
Java的浮点数遵从IEEE754标准,int转float,long转double都不一定是无损的(读者可以自己思考哪些情况下是无损的)。而byte、short、char转float是无损的,byte、short、char、int、float转double也是无损的。
boolean值域只有两个值:false,true。
float、long、和double在赋值时,若所赋的值为数字常量后紧跟上"f"、"l"、"d"(大写也可)。
float赋值时,若所赋的值为浮点数常量,则必须在数字后边加“f”或“F”。
long在赋值时,若所赋的值为常整数,建议加上大写字母“L”,因为“l”在有些文本阅读器下,看着像“1”
啰嗦一句:注意越界和正负值域不对称。x > y 不完全等同于 -x y 不完全等同于 x - y > 0。当然,这一点在实际的编程中,基本是被忽略的。保留这种意识还是有必要的。
Java为基本数据类型设计了包装器,使得基本数据类型在必要时,能够参与到对象层次的操作中来。本文表中给出了各种基本数据类型的包装器。常见的使用这些包装器的场景,有泛型、对象引用等。泛型主要体现在容器的使用上,使得我们可以定义一个整形数字的链表,或者key-value的Map使用基本数据类型。
基本数据是可以直接访问的,而对象则只能通过引用来访问。Java对基本数据类型的设计方式,使得既可以实现高效编程,又能够建立对象模型,而不用担心对简单数据类型的性能会产生负面影响。
8,类方法以及私有方法
本系列讲座有着很强的前后相关性,如果你是第一次阅读本篇文章,为了更好的理解本章内容,笔者建议你最好从本系列讲座的第1章开始阅读,请点击这里 。
Objective-C里面区别于实例方法,和Java或者C++一样,也支持类方法。类方法(Class Method) 有时被称为工厂方法(Factory Method)或者方便方法(Convenience method)。工厂方法的称谓明显和一般意义上的工厂方法不同,从本质上来说,类方法可以独立于对象而执行,所以在其他的语言里面类方法有的时候被称为 静态方法。就像@interface曾经给我们带来的混乱一样,现在我们就不去追究和争论工厂方法的问题了,我们看到Objective-C的文章说工厂 方法,就把它当作类方法好了。
在Objective-C里面,最受大家欢迎的类方法应该是alloc,我们需要使用alloc来为我们的对象分配内存。可以想象,如果没有alloc,我们将要如何来为我们的类分配内存!
和其他的语言类似,下面是类方法的一些规则,请大家务必记住。
1,类方法可以调用类方法。
2,类方法不可以调用实例方法,但是类方法可以通过创建对象来访问实例方法。
3,类方法不可以使用实例变量。类方法可以使用self,因为self不是实例变量。
4,类方法作为消息,可以被发送到类或者对象里面去(实际上,就是可以通过类或者对象调用类方法的意思)。
如果大家观察一下Cocoa的类库,会发现类方法被大量的应用于方便的对象创建和操作对象的,考虑到类方法的上述的特性,同学们在设计自己的类的时候,为 了谋求这种方便,可以考虑使用类方法来创建或者操作对象。笔者认为,这个就是类方法的潜规则,在本章的范例程序里面,笔者将要遵守这个潜规则。
在上一章我们讲了一下实例变量的作用域,实例变量的作用域的方式和其他面向对象的语言没有什么不同。对于方法,非常遗憾的是,Objective- C并没有为我们提供诸如public,private和protected这样的限定,这就意味着在Objective-C里面,从理论上来说所有的方法 都是公有的。但是,我们可以利用Objective-C的语言的特性,我们自己来实现方法的私有化。当然我们自己的私有化手段没有得到任何的编译器的支 持,只是告诉使用者:“这是一个私有的方法,请不要使用这个方法”。所以,无论作为类的设计者和使用者都应该清楚在Objective-C里面的方法私有 化的所有手段,这样就在类的设计者和使用者之间达成了一种默契,这种方式明显不是Objective-C语法所硬性规定的,所以也可以把这种手法成为一种 潜规则。
关于潜规则 经常看英文文档的同学,应该可以遇到这样一个词,de facto standard,也就是笔者所说的潜规则。
本章所述的方法的私有化是一种有缺陷的手段,有一定的风险而且也没有完全实现私有化,在后面的章节里面笔者会陆续的给出其他的实现方法私有化的方法。
另外,Objective-C里面有一个其他不支持指针的语言没有的一个动态特性,那就是程序在执行的时候,可以动态的替换类的手段。动态的方法替 换有很多种应用,本章实现了一个类似java里面的final函数。和final函数不同的是,如果子类重写了这个方法,编译器不会报错,但是执行的时候 总是执行的你的超类的方法。
类方法,方法私有化和动态方法替换将是本章的主题。
8.1,本章程序的执行结果在本章里面,我们将要继续使用我们在第4章已经构筑好的类Cattle和Bull。
笔者在这里暂时违反一下不修改已经生效的代码规则改写了一下Cattle和Bull类,在里面追加了一些类方法,用于创建Cattle系列的对象。
笔者也改写了Cattle的头文件用来实现方法的私有化。
面向对象的程序有一个很大的特色就是动态性,但是由于某种原因我们在设计超类的时候,也许会考虑把某个方法设定成为静态的,这样就有了诸如 final的概念。在本章我们将要使用动态的方法替换来实现这个功能。我们将要构筑一个新类,名字叫做UnknownBull,我们使用动态方法替换导致 即使UnknownBull重载了Cattle类的saySomething,但是向UnknownBull发送saySomething的时候,仍然执 行的是Cattle的saySomething。本章程序的执行结果请参照下图:
图8-1,本章程序的执行结果。
本章程序可以点击这里 下载。
8.2,实现步骤第一步,按照我们在第2章所述的方法,新建一个项目,项目的名字叫做07-InitWithAndIvarScope。如果你是第一次看本篇文章,请到这里 参看第二章的内容。
第二步,按照我们在第4章的4.2节的第二,三,四步所述的方法,把在第4章已经使用过的“Cattle.h”,“Cattle.m”,“Bull.h”还有“Bull.m”, 导入本章的项目里面。
第三步,打开“Cattle.h”和“Cattle.m”,分别修改成为下面的代码并且保存:
#import <Foundation/Foundation.h> @interface Cattle : NSObject { int legsCount; } - (void)saySomething; + (id) cattleWithLegsCountVersionA:(int) count; + (id) cattleWithLegsCountVersionB:(int) count; + (id) cattleWithLegsCountVersionC:(int) count; + (id) cattleWithLegsCountVersionD:(int) count; @end #import "Cattle.h" #import <objc/objc-class.h> @implementation Cattle -(void) saySomething { NSLog(@"Hello, I am a cattle, I have %d legs.", legsCount); } -(void) setLegsCount:(int) count { legsCount = count; } + (id) cattleWithLegsCountVersionA:(int) count { id ret = [[Cattle alloc] init]; //NEVER DO LIKE BELOW //legsCount = count; [ret setLegsCount:count]; return [ret autorelease]; } + (id) cattleWithLegsCountVersionB:(int) count { id ret = [[[Cattle alloc] init] autorelease]; [ret setLegsCount:count]; return ret; } + (id) cattleWithLegsCountVersionC:(int) count { id ret = [[self alloc] init]; [ret setLegsCount:count]; return [ret autorelease]; } + (id) cattleWithLegsCountVersionD:(int) count { id ret = [[self alloc] init]; [ret setLegsCount:count]; if([self class] == [Cattle class]) return [ret autorelease]; SEL sayName = @selector(saySomething); Method unknownSubClassSaySomething = class_getInstanceMethod([self class], sayName); //Change the subclass method is RUDE! Method cattleSaySomething = class_getInstanceMethod([Cattle class], sayName); //method_imp is deprecated since 10.5 unknownSubClassSaySomething->method_imp = cattleSaySomething->method_imp; return [ret autorelease]; } @end
第四步,打开“Bull.h”和“Bull.m”,分别修改成为下面的代码并且保存:
#import <Foundation/Foundation.h> #import "Cattle.h" @interface Bull : Cattle { NSString *skinColor; } - (void)saySomething; - (NSString*) getSkinColor; - (void) setSkinColor:(NSString *) color; + (id) bullWithLegsCount:(int) count bullSkinColor:(NSString*) theColor; @end #import "Bull.h" @implementation Bull -(void) saySomething { NSLog(@"Hello, I am a %@ bull, I have %d legs.", [self getSkinColor],legsCount); } -(NSString*) getSkinColor { return skinColor; } - (void) setSkinColor:(NSString *) color { skinColor = color; } + (id) bullWithLegsCount:(int) count bullSkinColor:(NSString*) theColor { id ret = [self cattleWithLegsCountVersionC:count]; [ret setSkinColor:theColor]; //DO NOT USE autorelease here! return ret; } @end
第五步,创建一个新类,名字叫做“UnknownBull”,然后分别打开“UnknownBull.h”和“UnknownBull.m”,分别修改成为下面的代码并且保存:
#import <Foundation/Foundation.h> #import "Bull.h" @interface UnknownBull : Bull { } -(void)saySomething; @end #import "UnknownBull.h" @implementation UnknownBull -(void)saySomething { NSLog(@"Hello, I am an unknown bull."); } @end
第六步,打开“08-Class_Method_And_Private_Method.m” ,修改成为下面的样子并且保存
#import <Foundation/Foundation.h> #import "Cattle.h" #import "Bull.h" #import "UnknownBull.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; id cattle[5]; cattle[0] = [Cattle cattleWithLegsCountVersionA:4]; cattle[1] = [Bull cattleWithLegsCountVersionB:4]; cattle[2] = [Bull cattleWithLegsCountVersionC:4]; cattle[3] = [Bull bullWithLegsCount:4 bullSkinColor:@"red"]; cattle[4] = [UnknownBull cattleWithLegsCountVersionD:4]; for(int i = 0 ; i < 5 ; i++) { [cattle[i] saySomething]; } [pool drain]; return 0; }
第七步,选择屏幕上方菜单里面的“Run”,然后选择“Console”,打开了Console对话框之后,选择对话框上部中央的“Build and Go”,如果不出什么意外的话,那么应该出现入图8-1所示的结果。如果出现了什么意外导致错误的话,那么请仔细检查一下你的代码。如果经过仔细检查发现 还是不能执行的话,可以到这里 下载笔者为同学们准备的代码。 如果笔者的代码还是不能执行的话,请告知笔者。
8.2,方法的私有化在讲述方法私有化之前,我们首先要提到一个Objective-C里面的一个概念,动态类型和静态类型。
所谓的动态类型,就是使用id来定义一个对象,比如说
id cattle = [[Cattle alloc] init];
所谓的静态类型,就是使用已知变量的的类型来定义对象,比如说
Cattle cattle = [[Cattle alloc] init];
动态类型和静态类型各有好处,动态类型实现了多态性,使用静态类型的时候编译器会为你检查一下也许会出现危险的地方,比如说向一个静态类型的对象发送一个它没有定义的消息等等。
好的,我们现在打开“cattle.h”,大家可以发现,和以前的版本相比,我们的“cattle.h”少了一个方法的定义,那就是-(void) setLegsCount:(int) count;。笔者在本章的范例程序里面实现私有方法的手段比较简单,直接把-(void) setLegsCount:(int) count从“cattle.h”给删除掉了。
大家打开““cattle.m”,可以看到里面-(void) setLegsCount:(int) count是有实现部分的。实现部分和过去的版本没有任何区别的。
我们本章里面讲述的实现方法私有化的手段,就是从头文件当中不写方法的声明。这样做会导致如下几个现象
1,在类的实现文件.m里面,你可以向平常一样使用[self setLegsCount:4] 来发送消息,但是确省设定的编译器会很不礼貌的给你一个警告。
2,你可以向Cattle以及从Cattle继承的类的静态对象发送setLegsCount:4的消息,但是同样,确省设定的编译器会很不礼貌的给你一个警告。
3,你可以向Cattle以及从Cattle继承的类的动态对象发送setLegsCount:4的消息,编译器不会向你发送任何警告的。
说到这里,同学们也许会觉得这一节的方法私有化有一点奇怪,因为在上面的第二条里面,不能阻止对对象的私有方法进行调用。令我们更为恼火的是,居然在我们自己的类的实现文件里面需要调用的时候产生诸如第一条的警告!
让我们冷静一下。
我们说,在面向对象的程序里面,一般而言类的使用者只关心接口,不关心实现的。当我们类的实现部分的某个方法,在头文件里面没有定义的话,那么由于 我们的类的使用者只是看头文件,所以他不应该是用我们定义的所谓的私有方法的。这一点,对于其他的语言来说也是一样的,其他的语言的私有方法和变量,如果 我们把它们改为public,或者我们不修改头文件,使用指针也可以强行的访问到私有的变量和方法的,从这个角度上来说,私有化的方法和变量也只不过是一 个摆设而已,没有人可以阻止我们去访问他们,探求埋藏在里面的奥秘。所谓的私有化只不过是一个潜规则而已,在正常的时候,我们大家都会遵守这个潜规则的。 但是被逼无奈走投无路的时候我们也许会除了访问私有的东西无可选择。但是也不能过分,我们显然不可以把访问私有变量和函数当作一种乐趣。
说到这里,我想大家应该可以理解这种私有化方法的定义了。它只不过是一种信号,告诉类的使用者,“这是一个私有的函数,请不要使用它,否则后果自 负” 。我们在看到别人的代码的时候看到了这种写法的时候,或者别人看到我们的代码的时候,大家都需要做到相互理解对方的隐藏私有部分的意图。还是还是这句话, 在大多数时候,请不要破坏潜规则。
8.3, 类方法我们现在转到本章最重要的主题,类方法。我们将要首先关注一下类方法的声明,现在请同学们打开"Cattle.h"文件,可以发现下面的代码:
+ (id) cattleWithLegsCountVersionA:(int) count; + (id) cattleWithLegsCountVersionB:(int) count; + (id) cattleWithLegsCountVersionC:(int) count; + (id) cattleWithLegsCountVersionD:(int) count;
类方法和实例方法在声明上的唯一的区别就是,以加号+为开始,其余的部分是完全一致的。 笔者在这里定义了4个不同版本的类方法,从功能上来说都是用来返回Cattle类或者其子类的对象的,其中 cattleWithLegsCountVersionA到C是我们这一节讲解的重点。
让我们首先打开“Cattle.m” ,关注一下下面的代码:
+ (id) cattleWithLegsCountVersionA:(int) count { id ret = [[Cattle alloc] init]; //NEVER DO LIKE BELOW //legsCount = count; [ret setLegsCount:count]; return [ret autorelease]; } + (id) cattleWithLegsCountVersionB:(int) count { id ret = [[[Cattle alloc] init] autorelease]; [ret setLegsCount:count]; return ret; }
我们需要使用类方法创建对象,所以在第3行,我们使用了我们比较熟悉的对象的创建的方法创建了一个对象。大家注意一下第5行,由于类方法是和对象是 脱离的所以我们是无法在类方法里面使用实例变量的。第6行,由于我们创建了对象ret,所以我们可以向ret发送setLegsCount:这个消息,我 们通过这个消息,设定了Cattle的legsCount实例变量。在第7行,我们遇到了一个新的朋友,autorelease。我们在类方法里面创建了 一个对象,当我们返回了这个对象之后,类方法也随之结束,类方法结束就意味着在我们写的类方法里面,我们失去了对这个对象的参照,也就永远无法在类方法里 面控制这个对象了。在Objective-C里面有一个规则,就是谁创建的对象,那么谁就有负责管理这个对象的责任,类方法结束之后,除非和类的使用者商 量好了让类的使用者释放内存,否则我们无法直接的控制这个过程。
记忆力好的同学应该可以回忆起来,笔者曾经在第二章提到过一种延迟释放内存的技术,这个就是autorelease。关于autorelease以 及其他的内存管理方法,我们将在下一章放到一起讲解。到这里大家记住,使用类方法的潜规则是你要使用类方法操作对象,当你需要使用类方法创建一个对象的时 候,那么请在类方法里面加上autorelease。
我们来看看cattleWithLegsCountVersionB的实现部分的代码,和cattleWithLegsCountVersionA 唯一区别就是我们在创建的时候就直接的加上了autorelease。这样符合创建对象的时候“一口气”的把所有需要的方法都写到一起的习惯,采取什么方 式取决于个人喜好。
我们再打开“08-Class_Method_And_Private_Method.m”,参看下面的代码
cattle[0] = [Cattle cattleWithLegsCountVersionA:4]; cattle[1] = [Bull cattleWithLegsCountVersionB:4];
我们在回头看看本章程序的执行结果,心细的同学也许发现了一个很严重的问题,我们在第2行代码里面想要返回一个Bull的对象,但是输出的时候却变 成了Cattle,原因就是我们在cattleWithLegsCountVersionB里面创建对象的时候,使用了id ret = [[[Cattle alloc] init] autorelease]。由于Bull里面没有重写cattleWithLegsCountVersionB,所以除非我们重写 cattleWithLegsCountVersionB否则我们向Bull发送cattleWithLegsCountVersionB这个类方法的时 候,只能得到一个Cattle的对象。我们可以要求我们的子类的设计者在他们的子类当中重写cattleWithLegsCountVersionB,但 是这样明显非常笨拙,失去了动态的特性。我们当然有办法解决这个问题,现在请大家回到“Cattle.m”,参照下列代码:
+ (id) cattleWithLegsCountVersionC:(int) count { id ret = [[self alloc] init]; [ret setLegsCount:count]; return [ret autorelease]; }
我们的就在第3行,我们不是用静态的Cattle,而是使用self。说到这里也许大家有些糊涂了,在其他的语言当中和self比较类似的 是this指针,但是在Objective-C里面self和this有些不大一样,在类函数里面的self实际上就是这个类本身。大家可以打开 debugger观察一下,self的地址就是Bull的Class的地址。所以程序执行到上面的代码的第3行的时候,实际上就等同于id ret = [[[Bull class] alloc] init];
我们可以在类方法里面使用self,我们可否通过使用self->legsCount来访问实例变量呢?答案是不可以,因为在这个时候对象没有被创建也就是说,没有为legsCount分配内存,所以无法访问legsCount。
由于Bull类在程序被调入内存的时候就已经初始化好了,Bull类里面的实例函数应该被放到了代码段,所以从理论上来说,我们可以通过使用 [self setLegsCount:count]来调用实例方法的,但是不幸的是Objective-C没有允许我们这样做,我们在类方法中使用self来作为消 息的接收者的时候,消息总是被翻译成为类方法,如果发送实例方法的消息的话,会在执行的时候找不到从而产生异常。这样做是有一定的道理的,因为一般而言, 实例方法里面难免要使用实例变量,在类方法当中允许使用实例方法,实际上也就允许使用实例变量。
关于self 大家需要记住下面的规则:
1,实例方法里面的self,是对象的首地址。
2,类方法里面的self,是Class.
尽管在同一个类里面的使用self,但是self却有着不同的解读。在类方法里面的self,可以翻译成class self;在实例方法里面的self,应该被翻译成为object self。在类方法里面的self和实例方法里面的self有着本质上的不同,尽管他们的名字都叫self。
请同学们再次回到图8-1,可以发现通过使用神奇的self,我们动态的创建了Bull类的对象。但是等一下,我们的程序并不完美,因为Bull类 的skinColor并没有得到初始化,所以导致了null的出现。我们在设计Cattle类也就是Bull的超类的时候,明显我们无法预测到Bull类 的特征。消除这种问题,我们可以在得到了Bull对象之后使用setSkinColor:来设定颜色,当然我们也可以直接写一个Bull类的方法,来封装 这个操作,请同学们打开“Bull.h”:
我们追加了一个类方法, bullWithLegsCount:bullSkinColor:用于创建Bull对象,请同学们打开“Bull.m”:
+ (id) bullWithLegsCount:(int) count bullSkinColor:(NSString*) theColor { id ret = [self cattleWithLegsCountVersionC:count]; [ret setSkinColor:theColor]; //DO NOT USE autorelease here! return ret; }
上面这一段代码相信大家都可以看明白,笔者就不在这里赘述了。但是笔者需要强调一点,在这里我们不需要调用autorelease的,因为我们没有在这里创建任何对象。
经过了这个改造,通过在“08-Class_Method_And_Private_Method.m”里面我们使用
使得我们的代码终于正常了,请参照图8-1的第4行输出。
8.4,使用动态方法替换实现final功能首先请同学们打开“Cattle.m”,参照下面的代码片断:
+ (id) cattleWithLegsCountVersionD:(int) count { id ret = [[self alloc] init]; [ret setLegsCount:count]; if([self class] == [Cattle class]) return [ret autorelease]; SEL sayName = @selector(saySomething); Method unknownSubClassSaySomething = class_getInstanceMethod([self class], sayName); //Change the subclass method is RUDE! Method cattleSaySomething = class_getInstanceMethod([Cattle class], sayName); //method_imp is deprecated since 10.5 unknownSubClassSaySomething->method_imp = cattleSaySomething->method_imp; return [ret autorelease]; } @end
在cattleWithLegsCountVersionD里面,我们将要通过使用动态的方法替换技术来实现final方法。
第3,4行代码,是用于创建Cattle或者从Cattle类继承的对象,并且设定实例变量legsCount。
第6,7行代码,是用来判断调用这个类方法的self是不是cattle,如果是cattle的话,那么就直接返回,因为我们要在这个方法里面把子
类的saySomething替换成为Cattle的saySomething,如果类是Cattle的话,那么很明显,我们不需要做什么事情的。
第9行代码是老朋友了,我们需要得到方法的SEL。
第10行和第12行,我们需要通过Objective-C的一个底层函数,class_getInstanceMethod来取得方法的数据结构 Method。让我们把鼠标移动到Method关键字上面,点击鼠标右键盘,选择“Jump to definition”,我们可以看到在文件“objc-class.h”里面的Method的定义。Method实际上是类方法在Class里面的数据 结构,系统会使用Method的信息来构筑Class的信息。在Method类型的声明里面,我们看到了下面的代码
typedef struct objc_method *Method; struct objc_method { SEL method_name; char *method_types; IMP method_imp; };
其中SEL和IMP我们已经很熟悉了,method_types是方法的类型信息,Objective-C使用一些预定义的宏来表示方法的类型,然后把这些信息放到 method_types里面。
需要强调的是,苹果在10.5之后就降级了很多Objective-C 底层的函数,并且在64位的应用当中使得这些函数失效,笔者对剥夺了众多程序员的自由而感到遗憾。
第14行的代码,我们把子类的函数指针的地址替换成为Cattle类的saySomething,这样无论子类是否重写saySomething, 执行的时候由于runtime需要找到方法的入口地址,但是这个地址总是被我们替换为Cattle的saySomething,所以子类通过 cattleWithLegsCountVersionD取得对象之后,总是调用的Cattle的saySomething,也就实现了final。当 然,这种方法有些粗鲁,我们强行的不顾后果的替换了子类的重写。
重要 本节提到的final的实现方法,没有任何苹果官方的文档建议这样做,纯属笔者自创仅供大家参考,如果使用风险自担。
替换的结果,就是虽然我们在“08-Class_Method_And_Private_Method.m”里面的cattle[4]l里面使用 UnknownBull是图返回UnknownBull对象,我们也确实得到了UnknownBull对象,但是不同的是,我们在 cattleWithLegsCountVersionD里面狸猫换太子,把UnknownBull的saySomething变成了Cattle的 saySomething。
让我们回到图8-1,我们发现最后一行的输出为Cattle的saySomething。
关于final的实现方式,我们当然可以使用一个文明的方法来告知子类的使用者,我们不想让某个方法被重写。我们只需要定义一个宏
类的使用者看到这个FINAL之后,笔者相信在绝大多数时候,他会很配合你不会重写带FINAL定义的方法的。
8.5,本章总结我们在本章里面讲述了方法私有化,类方法的定义和使用,动态方法替换等技术手段,也给大家强调和澄清了self的概念。
更重要的是,笔者向大家介绍了一些潜规则,希望大家可以遵守。
非常感谢大家这些天对我的鼓励以及支持!
来源:http://www.cnblogs.com/yaski/archive/2009/04/29/1444035.html
前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例。 前置减号(-)的方法为实例方法,必须使用类的实例才可以调用的。
9.1前言
内存管理在objective-C 2.0中是非常重要的,内存管理做得好不好,最直观的就是反应在你的程序crash上面。想要你的程序健壮稳定,那么请务必搞明白内存管理。
9.2 objective-C 2.0是怎样一个内存机制?
在objective-C 2.0语言中,内存管理主要是基于一个 release count的值来进行判断系统是否要回收该内存。当某对象的 retain count等于0时,系统则会回收这段内存。所以当我们想要释放掉某项目时,我们只需要使其retain count等于0即可。当我们想要hold住该段内存以便后面继续使用(具体怎么用将会在后面提到)那么你只需要保证在你要使用它之前 它的retain count>0即可。为何苹果要这样做?等下告诉你。
首先还是说明 retain count是如何去操作的。假设我们有一个class A:NSObject,然后我们声明一个变量 A *a,不用在意它是临时变量还是类属性变量,这时候它的retain count等于0;当我们给这个变量创建内存空间的时候 我们会使用NSObject类的 alloc 函数:a=[A alloc];这时候 a的retain count则会+1,能使 retain count +1的函数不止有alloc,还有 [a retain],和[a copy],关于这两个函数会在什么时候使用以及他们的区别,将在后面讨论。要想使retain count -1 你需要调用函数 [a release];那么写一串代码来直观的表示retain count的变化:
#import "A.h" /** 略**/ -(void) retainCountDemo { A *a; //retain count of a is 0 a = [A alloc]; //retain count of a is 1 [a retain]; //retain count of a is 2 [a release]; //retain count of a is 1 A* a1 = [a copy]; //retain count of a1 is 1,and a's is 1 too [a release]; //retain count of a is 0, and it will be dealloced [a1 release]; //retain count of a1 is 0, and it will be dealloced return; }
9.3 copy 和 retain的区别?
copy和retain的区别在于它们的字面意思,不同之处在于一个是直接引用(比如说对象的引用),然后retain count+1,另外一个是复制使用(比如说字符串的使用),将复制到的对象a1的retain count+1.
9.4 他们有什么用?
objective-C 2.0是一门面向对象的语言,通过使用 retain count来就可以让开发者在各自的模块中创建使用以后就“释放”,而不用担心影响到其他模块如果同时会使用到该变量而造成的crash。最典型也是最常 用的就是我们的NSURLConnection类里面的request,这种异步请求,你并不知道什么时候该释放,因为你没法判断什么时候请求能够返回, 所以通过使用retain count 你只关心你创建了一个 NSURLConnction,在设置完接收对象,发送完请求以后你就可以release掉它了,而无需关心内存泄露之类的问题。因为在发送请求的时 候,NSURLConnction会将自己retain一次,这时候的retain count变为2,你在自己的函数中调用release的时候retaincount则变为1,而当该请求返回的时候,完成回调函数,会自动调用 release一次,这时候NSURLConnction的retain count变为0而释放掉。
通过上面的例子来总结,我们 可以发现,其实这是一种延迟释放的机制(好吧,这个词是我自己想到的)。依照这个我们可以解决很多对象共用而不知道该谁去管理内存的问题,苹果的做法就 是:管好你自己,你自己alloc+retain多少次,那么请你release多少次。所以,内存管理最基本的就是管好你自己!
9.5 内存管理有没有更简单点的方法呢?
有!但是不建议使用。
方法1:autorelease函数,通过函数名你应该可以知道这个是帮你自动release的。但是需要注意的是这个函数只会自动帮你release1 次,你如果在中间使用了retain之类的,所以还请手动release。同时这个有一个致命的缺点,你想用autorelease在class A中创建对象然后传递给class B使用的话这是非常危险的,autorelease是基于系统自带的自动释放池来进行内存管理,系统会每隔一段时间去检测施放池中的对象,并且释放不在使 用的对象。当你传递给B的时候,还没来得及使用,被自动释放掉了,那么你的程序又会crash。所以 autorelease通常都是在局部对象中使用。
方法2:IOS 5 ARC(Automatic Reference Counting),苹果终于发现了做IOS开发有太多无证程序猿了,经常因为内存释放不好而导致野指针,内存泄露,各种问题的存在。所以苹果在IOS5 的SDK中加了这么一个东西,自动引用计数器。好了,这下各位可以放心的使用,而不用麻烦的去数retain release了。如果你能保证你的客户都会乖乖升级到IOS5的话…… 显然,暂时这东西还不太靠谱……
9.6 稍微高级点的应用:
于是我就只想到一个大量生成的临时对象和自动释放池这两个名词,具体如何使用,请去查阅苹果API,keywords:NSAutoreleasePool。
9.7 后记:
上面所写都是我自己对Objective-C的理解和自己以前所犯错误而总结出来的经验,遗憾的是正好我高考语文考试不及格,所以写的文章也不太好懂,还 请见谅,如果有什么疑问或者意见,还请指出一起讨论学习。实例代码,非常简单,如果有朋友看了也不太明白,还请自己写demo验证。对于委托 (delegate),通知(notification),属性等这些东西本来是想也在这里说一说的,但是因为思维跳跃比较大,所以还是抽时间另外写出 来。
来源: http://www.cnblogs.com/yaski/archive/2012/01/12/2320000.html