参考此文:http://www.2cto.com/kf/201403/284687.html
@
看到这个关键字,我们就应该想到,这是Object-C对C语言的扩展,例如@interface XXX。
@interface
声明类
@implementation
实现类
@protocol
声明协议
@optional
与@protocol配合使用,说明协议中的某个或者某几个方法可以不实现
@required
与@protocol配合使用,说明协议中的某个方法或者某几个方法必须实现
@end
与@interface ,@implementation,@protocol配合使用,代表声明或者实现结束
@encode
@encode为编译器宏,它可以将类型转换为相应的字符串。
id
id是指向Objective-C类对象的指针,它可以声明为任何类对象的指针,当在Objective-C中使用id时,编译器会假定你知道,id指向哪个类的对象。与void是不同的是,void编译器不知道也不假定指向任何类型的指针。
nil
定义为一个常量,如果一个指针的值为nil,代表这个指针没有指向任何对象。
self
在Objective-C中,关键字self与c++中this是同一概念,就是类对象自身的地址,通过self可以调用自己的实例变量和方法
Super
当子类需要调用父类的方法时,会用到Super关键字. Super指向的是父类的指针,子类重写父类的方法时,调用父类的方法是一个比较好的习 惯。因为 当我们不知道父类在该方法中实现的功能时,如果不调用父类的方法,有可能我们重写的方法会失去该功能,这是我们不愿意看到的情况。
NSNull
NSNull是没有的意思,如果一个字典的值为NSNull,那说明与该值对应的Key是没有值的,例如Key为address,说明与address对应的是值是没有。
self super class public protected private id
[self class] [super class] selector
objective-c runtime reference
标准用法
self = [super init]
new
1 Objective-C有一个特性,就是可以把类当成对象来发送消息,这种用法通常用于新建对像时,例如 XXX *object = [XXX new];
类方法 +
如果想声明属于类而不属于类对象的方法,用+。+用来修饰类的方法,使用+修饰的类方法,是整个类的方法,不属于哪一个类对象,这与C++中的static在类中使用的概念一样,
%@
在NSLog中,使用%@表示要调用对象的description方法。
概念
类
是一种结构,它表示对象的类型,就像int与 char 一样,也可以声明类的变量(对像)
实例化
为类的对象分配内存和初始化,达到可以使用该 类对象的目的。
对象(实例)
类的实例化后的产物
消息
在Object-C中,类的对象执行的操作,是通过给该类或者该类对象发送消息实现,如:[object func];就是给object对象发送 func消息,类似C++中的方法调用。给object对象发送func消息后,object对象查询所属类的func方法执行。
方法调度
当向一个对象发送消息时(调用方法),这个方法是怎么被调用的呢?这就依赖于方法高度程序,方法调度程序查找的方法如下:
在本类的方法中,找被调用的方法,如果找到了,就调用,如果找不到被沿着继承路径去查找,从哪个类找到,就调用哪个类的方法,如果到最根上的类还是没有找到,那编译就会出错。
继承与复合
在Objective-C中支持继承,但只是支持单一继承(有且只有一个父类有),如果想使用多继承的特性,可以使用分类和协议技术。
继承是is-a,复合是has-a。复合是通过包含指向对象的指针实现的,严格意义上讲,复合是针对于对象间来说,对于基本数据类型来说,它们被认为是对象的一部分。
装箱与拆箱
由于NSArray,NSDirectory等类不能直接存储基本数据类型,所以要想在NSArray\NSDirectory中使用基本数据类型,就得使用装箱与拆箱。
在Objective-C中,可以使用NSNumber和NSValue来实现对数据类型的包装,NSNumber可以实现对基本数据类型的包装,NSValue可以实现对任意类型数据的包装。
将基本类型封装成对象叫装箱,从封装的对象中提取基本类型叫拆箱(取消装箱),其它语言如Java原生支持装箱与拆箱,Ojbective-C不支持自动装箱与拆箱,如果需要得需要自己来实现装箱与拆箱。
存取方法
在使用类对象的实例变量(成员数据)时,不要直接使用对象中的实例,要使用存以方法来获取或者修改实例,既setter和getter,在 Cocoa中, 存取方法有命名习惯,我们得符合这种习惯,以便于与其它团队成员合作。setter方法是修改或者设置实例值,命名习惯为set+实例名,例有一个类有 path实例变量,那setter命名为setPath,getter命名为Path,为什么不是getPath,因为get在Cocoa中有特殊的含 义,这个含义就是带有get的方法就意味着这个方法通过形参指针(传入函数的参数指针)来返回值。我们要遵守这个命名习惯或者说规则。
在Objective-C 2.0中加入了@property和@synthesize来代替setter和getter,这两个关键字为编译器指令。还有点表达式,存取类成员的值时,可以使用点表达式。
Object.attribute,当点表达式在=号左边时,调用的是setter方法,在=号右边时,调用的是getter方法。
@property 语法为:@property (参数) 类型 变量名.
在这里主要说明一下参数.
参数分为三种:
第一种:读写属性包括(readonly/readwrite/)
第二种:setter属性(assign,copy,retain),assign是简单的赋值,copy是释放旧成员变量,并新分配内存地址给成 员 变量,将传入参数内容复制一份,给成员变量。retain是将传入 参数引用计数加1,然后将原有的成员变量释放,在将成员变量指向该传入参数。
第三种:与多线程有关(atomic,nonatomic).当使用多线程时,使用atomic,在不使用多线程时使用nonatomic
对象创建与初始化
在Objective-C中创建对象有两种方法,一种是[类 new];另一种是[[类 alloc] init],这两种方法是等价的,但按惯例来讲,使用[[类 alloc] init];
alloc操作是为对象分配内存空间,并将对象的数据成员都初始,int 为0,BOOL 为NO, float 为0.0等。
初始化,默认的初始化函数为init,init返回值为id,为什么回返回id呢,因为要实现链式表达式,在Objective-C中叫嵌套调用。
为什么要嵌套调用??因为初始化方法init返回值可能与alloc返回的对象不是同一个?为什么会发生这种情况?基于类簇的初始化,因为init可以接受参数,在init内部有可能根据不同的参数来返回不同种类型的对象,所以最会发生上面说的情况。
在初始化时,建议使用if (self = [super init])
便利初始化
当一个类需要根据不同的情况来初始化数据成员时,就需要便利初始化函数,与init初始化不同的是,便利初始化函数有参数,参数个数可以有1到N个,N是类数据成员个数。
指定初始化函数:什么是指定初始化函数?在类中,某个初始化函数会被指定为指定的初始化函数,确定指定初始化函数的规则是初始化函数中,参数最多的为指定初始化函数,其它未被指定为指定初始化函数的初始化函数要调用指定初始化函数来实现。对于该类的子类也是一样,只要重写或者直接使用父类的指定初始化函数。上述文字有些绕,来个例子吧
@interface A{
int x;
int y;
}
-(id) init;
-(id) initWithX:(int) xValue;
-(id) initWithY:(int) yValue;
-(id) initWithXY:(int) xValue
yVal:(int) yValue;
@end
这里initWithXY被确定为指定初始化函数。
-(id) initWithXY:(int) xValue
yVal:(int) yValue{
if (self = [super init]){
x = xValue;
y = yValue;
}
return self;
}
-(id) init{
if (self = self initWithXY:10
yVal:20){
}
return self;
}
.......
@interface B: A{
int z;
}
-(jd) initWithXY......;
@end
@implementation B
-(id) initWithXY:(int) xValue
yVal:(int) yValue{
if (self = [super initWithXY:10
yVal=20]){
z= 40;
}
return self;
}
@end
自动释放池
内存管理是软件代码中的重中之重,内存管理的好坏,直接影响着软件的稳定性。在Cocoa中,有自动释放池,这类似于C++中的智能指针。
NSObject有一个方法是autorelease,当一个对象调用这个方法时,就会将这个对象放入到自动释放池中。
drain,该方法是清空自动释放池,不是销毁它。drain方法只适用于Mac OS X 10.4以上的版本,在我们写的代码中要使用release,release适用于所有版本。
自动释放池是以栈的方式实现,当创建一个自动释放池A时,A被压入栈顶,这时将接入autorelease消息的对象放入A自动释放池,这时创建一 个新的 B自动释放池,B被压入栈顶,创建完成后删除B,这个接收autorelease消息的对象依然存在,因为A自动释放池依然存在。
引用计数
每个对象都有一个与之相应的整数,称它为引用计数,当该引用计数为0时,Objective-C自动向该对象发送dealloc,以销毁该对向,与该引用计数相关的方法(消息)有下面几个
1 增加引用计数:通过alloc,new,copy创建一个对象时,该对象的引用计数加1(其实就是1,因为之前为0)
2 增加引用计数: retain
3 减少引用计数: release
局部分配内存(临时对象):
1 如果使用alloc,new,copy创建对象,则需要主动调用对象的release方法
2 如果使用非alloc,new,copy创建对象,我们认为该对象引用计数为1,并已经加入了自动释放池,我们不需要主动的调用对象的release方法。
拥有对象(在类中以成员的方法存在):
1 如果使用alloc,new,copy创建对象,则需要在dealloc方法中,释放该对象
2 如果使用非alloc,new,copy创建对象,则在拥有该对象时,保留该对象(执行retain方法),在dealloc方法中,释放该对象。
dealloc
当对象的引用计数为0时,Objective-C会自动发送对象的dealloc消息(自动调用对象的dealloc方法,类似于C++的析构函数),所以我们可以自己重写dealloc方法,来实现类里的对其它使用资源的释放工作。
注意:不要直接在代码中显示调用dealloc方法。
垃圾回收
在Objective-C 2.0中引入了垃圾回收机制(自动管理内存),在工程设置里设置Objective-C Garbage Collection为Required[-fobjc-gc-only]就可以使用垃圾回收机制。
启用垃圾回收机制后,通常的内存管理命令都变成了空操作指令,不执行任何操作。
Objective-C的垃圾回收机制是一种继承性的垃圾回收器,垃圾回收器定期检查变量和对象以及他们之间的指针,当发现没有任何变量指向对象时,就将该对象视为被丢弃的垃圾。所以在不在使用一个对象时,将指针他的指针设置为nil,这时垃圾回收器就会清理该对象。
注意:如果开发iPhone软件,则不能使用垃圾回收。在编写iPhone软件时,Apple公司建议不要在自己的代码中使用autorelease方法,并且不要使用创建自动释放对象的函数。
类别
什么是类别?类别是一种为现有类添加新方法的方式。
为什么使用类别或者说使用类别的目的是什么?有以下三点:
第一,可以将类的实现分散到多个不同的文件或多个不同的框架中。
如果一个类需要实现很多个方法,我们可以将方法分类,把分好的类形成类别,可以有效的管理和驾驭代码。
第二,创建对私有方法的前向引用。
第三,向对象添加非正式协议。
委托
委托的意思就是你自己想做某事,你自己不做,你委托给别人做。
在Ojbective-C中,实现委托是通过类别(或非正式协议)或者协议来实现。
举个例子:Apple要生产iPhone,Apple自己不生产(种种原因,其中之一就是在中国生产成本低,他们赚的银子多),Apple委托富士 康来生 产,本来富士康原来不生产iPhone,现在要生产了,所以他得自己加一个生产iPhone的生产线(类别,增加生产iPhone方法),这就是通过类别 来实现委托。下面用代码来说明这个例子。
....
Apple *apple = [[Apple alloc ] init];
Foxconn *fox = [[Foxconn alloc] init];
[apple setDelegate:fox];
[apple produceIPhone];
........
@implementation Apple
-(...) setDelegate:(id) x{
delegate = x; //! 将委托的生产对象指定为x
}
-(...) produceIPhone{
[delegate produceIPhone]; //! 委托对象生产iPhone
}
@interface Foxconn : NSObject
...
@end
@interface NSObject(ProduceIPhone) //! Foxconn之前就可以生产其它产品,有过声明和定义
-(...) produceIPhone //! 增加生产iPhone能力
@end
@implementation NSObject(ProduceIPhone)
//! 生产iPhone
-(...) produceIPhone{
......
}
@end
非正式协议
创建一个NSObject的类别, 称为创建一个非正式协议。为什么叫非正式协议呢?
也就是说可以实现,也可以不实现被委托的任务。
拿上面的例子来说,Apple要求Foxconn除了能生产iPhone外,还有一个要求是在一定时间内完成.由于双方没有签合同,所以时间要求和生产要求规格都是非正式协议
选择器
选择器就是一个方法的名称。选择器是在Objective-C运行时使用的编码方式,以实现快速查找。可以使用@selector预编译指令,获取 选择器 @selector(方法名)。NSObject提供了一个方法respondsToSelector:的方法,来访问对象是否有该方法(响应该消息)。
拿上面的Apple请Foxconn生产iPhone为例,Apple怎么知道Foxconn有没有生产iPhone的能力呢?Apple就通过 respondsToSelector方法询问Foxconn,是否可以生产iPhone(是否可以响应produceIPhone),询问结果是可以, 那Apple就委托Foxconn生产,Foxconn就生产出来了人们比较喜欢的iPhone产品。
正式协议
与非正式协议比较而言,在Ojbective-C中,正式协议规定的所有方法必须实现。在Ojbective-C2.0中,Apple又增加了两个关键字,协议中的方法也可以不完全实现,是哪个关键字见关键字部份的@optional,@required。
正式协议声明如下:
@protocol XXX
-(...) func1;
-(...) func2;
@end
使用协议:
@interface Object : NSObject //! Object从NSObject派生,并遵循XXX协议,要实现func1,func2函数。
...
@end
习惯用法
分配内存和初始化
self = [super init];
对象间交互
在Objective-C中,所有对象间的交互都是通过指针实现。
快速枚举
for (Type *p in array)
注意:
Objective-C不支持多继承
objective-c只不过是拥有一些附加特性的C语言。本质上就是C语言
1.C语言使用#include通知编译器应在头文件中查询定义。objective-c也可以使用#include来实现这个目的,但你永远不可能这么做,你会用#import,它是GCC编译器提供的,#import可以保证头文件只被包含一次。
xcode会使用预编译头文件(一种经过压缩的,摘要形式的头文件),在通过#import导入这种文件时,加载速度会非常快。
2.什么是框架
框架是一种聚集在一个单元的部件集合,包含头文件,库,图像,声音文件等。苹果公司将cocoa,Carbon,QuickTime和OpenGL 等技术 作为框架集提供。cocoa的组成部分有Foundation和Application Kit框架。还有一个支持框架的套件,包含 Core Animation和Core Image,这为Cocoa增添了多种精彩的功能。
每个框架都是一个重要的技术集合,通常包含数十个甚至上百个头文件。每个框架都有一个主头文件,它包含了所有框架的各个头文件。通过使用#import导入主头文件,可以使用所有框架的特性。
3.Foundation框架处理的是用户界面之下的层(layer)中得特性,例如数据结构和通信机制。
4.NS前缀
NS这个前缀告诉你函数来自cocoa而不是其他工具包。
两个不同事物使用相同标示符时会导致名称冲突,而前缀可以预防这个大问题。
5.BOOL类型
objective-c中得BOOL实际上是一种对带符号的字符类型(signed char)的定义。它使用8位存储空间,YES为1,NO为0.
6.间接
不在代码中直接使用某个值,而是使用指向该值的指针。另一个意思是,让别的类来完成本类的工作。
例子:
1.循环次数的变量。变量与间接
2.使用从文件读取。文件与间接
在OOP(面向对象编程)中,间接十分重要。OOP使用间接来获取数据,OOP真正的革命性就是它在调用代码中使用间接。比如在调用函数时,不是直接调用,而是间接调用。
7.过程式程序与OOP的区别
过程式程序建立在函数之上,数据为函数服务。面向对象编程从相反的角度来看待问题。它以程序的数据为中心,函数为数据服务。在OOP中,不在重点关注程序中得函数,而是专注与数据。
8.id
id是一种泛型,用于表示任何种类的对象。
9.OOP中得一些术语
类:类是一种结构,它表示对象的类型。对象引用类来获取和本身有关的各种信息,特别是运行什么代码来处理每种操作。
对象:对象是一种结构,它包含值和指向其类的隐藏指针。
实例:实例是“对象”的另一种称呼。
消息:消息是对象可以执行的操作,用于通知对象去做什么。
方法:方法是为响应消息而运行的代码。根据对象的类,消息可以调用不同的方法。
方法调度程序:是objective-c使用的一种机制,用于推测执行什么方法以响应某个特定的消息。
接口:接口是对象的类应该提供的特性的描述。接口不提供实现细节。
实现:实现是使接口正常工作的代码。
10.中缀符
objective-c有一种名为中缀符的语法技术。方法的名称及其参数都是合在一起的。例如: [trxtThing setStringValue:@"Hello there" color:kBlueColor]; 中 setStringValue: 和 color:实际上是参数的名称(实际上是方法名称的一部分)。使代码可读性更强,更容易理解参数的用途。
11.先行短线
-(void)draw;
前面的短线表明这是objective-c方法的生命。这是一种区分函数原型与方法声明的方式,函数原型中没有先行短线。-代表是实例方法。+代表是类方法。
12.@interface
创建某个特定类的对象之前,objective-c编译器需要一些有关该类的信息。他必须知道对象的数据成员和它提供的特性可以使用@interface指令把这种信息传递给编译器。用于定义类的公共接口。
13.@implementation
是一个编译器指令,表明你将为某个类提供代码。类名出现在@implementation之后。该行的结尾处没有分号。因为在objective-c编译器指令后不必使用分号。
@interface和@implementation间的参数名不同是正确的。
在@interface中没有声明却在@implementation中实现的方法是私有方法。
14.实例化
创建对象的过程叫做实例化。实例化对象时,需要分配内存,然后这些内存被初始化并保存一些有用的默认值,这些值不同于你在获得新分配内存时得到的随机值。内存分配和初始化完成后,就创建了一个新的对象实例。
15.继承
创建一个新类时,通常需要定义新类以区别于其他类以及现有类。使用继承可以定义一个具有父类所有功能的新类,它继承了父类的这些功能。
objective-c没有多继承。
创建一个新类时,其对象首先从自身的超类中继承实例变量,然后添加他们自己的实例变量。
超类
父类
子类
孩子类
重写
方法调度:objective-c的方法调度程序将子当前类中搜索响应的方法。如果调度程序无法在接受消息的对象类中找到响应的方法,它就会在该类的超类中进行查找。顺着继承链找到下一个超类进行查找,直到NSObject类中也没有该方法,则会出现运行时错误。
16.复合
对象引用其他对象时,可以利用其他对象提供的特性,这就是复合。
17.UML
UML是一种用图表表示类,类的内容以及他们之间关系的常见方法。
18.多态
使用更具体种类的对象(子类对象)代替一般类型(父类),这种能力称为多态性。
19.self
是一个指向接收消息的对象的指针。指向第一个实例变量isa。因为objective-c编译器已经看到了所有这些类的@interface声明,因此,它能直到对象中实力变量的布局,通过这些重要的信息,编译器可以产生代码并查找任何需要的实例变量。
基地址加偏移:编译器使用“基地址加偏移”机制实现奇妙的功能。给定的对象基地址,是指第一个实例变量的首个字节在内存中得位置,通过在该地址加上偏移地址,编译器就可以查到其他实例变量的位置。
20.间接寻址方式,直接寻址方式
21.super
objective-c提供某种方式来重写方法,并且仍然调用超类的实现方式。当需要超类实现自身的功能,同时在前面或者后面执行某些额外的工作时,这种机制非常有用。为了调用继承方法的实现,需要使用super作为方法调用的目标。
22.cocoa
cocoa实际上是由2个不同的框架组成的:Foundation Kit和 Application Kit。Application Kit包含了所有的用户接口对象和高级类。
Foundation Kit
23.NSAutoreleasePool
mian()函数创建了(通过alloc)并初始化(通过init)了一个NSAutoreleasePool实例。在mian()函数结尾,这个池被排空。这就是Cocoa内存管理的预览。
24.NSRange
typedef struct _NSRange {
unsigned int location;
unsigned int length;
}NSRange;
这个结构体用来表示相关事务的范围,通常是字符串里的字符范围或者数组里的元素范围。
25.三种赋值方式
1.NSRange range;
range.location = 17;
range.length = 4;
2.C语言的聚合结构赋值机制
NSRange range = {17, 4};
3.Cocoa提供的一个快捷函数NSMakeRange()
NSRange range = NSMakeRange(17, 4);
使用NSMakeRange()的好处是你可以在任何能够使用函数的地方使用它,例如在方法调用中将其当成参数传递。
26.几何数据类型
1.NSPoint 代表笛卡儿平面中得一个点(x, y).
typedef struct _NSPoint {
float x;
float y;
}NSPoint;
2.NSSize 用来存储长度和宽度
typedef struct NSSize {
float width;
float height;
}NSSize;
3.NSRect 矩形数据类型,它是由点和大小复合而成
typedef struct _NSRect {
NSPoint origin;
NSSize size;
}NSRect;
27.字符串NSString
stringWithFormat:就是一个工厂方法,它根据你提供的参数创建新对象。
length:长度
isEqualToString:比较字符串内容是否相同
compart:将接受对象和传递来的字符串逐个字符的进行比较。返回一个enum数据
NSCaseInsensitiveSearch:不区分大小写字符。
NSLiteralSearch:进行完全比较,区分大小写
NSNumericSearch:比较字符串的字符个数,而不是字符值。
-(NSRange)rangeOfString:(NSString *)aString;
返回的range.start为开始位置,range.length为长度。
28.NSMutableString可变字符串。
stringWithCapacity:创建一个新的NSMutableString
字符串的大小并不仅限于所提供的容量,这个容量仅是个最优值。如果要创建一个40mb的字符串。
NSMutableString *str = [NSMutableString stringWithCapacity:42];
appendString接受参数aString,然后将其复制到接收对象的末尾。
appendFormat与stringWithFormat:类似,但它将格式化的字符串附加在接收字符串的末尾,而不是创建新的字符串对象。
29.集合家族
1.NSArray:是一个Cocoa类,用来存储对象的有序列表。
两个限制:1.只能存储objective-c的对象,不能存储C语言中得基本数据类型。
2.也不能存储nil。
30.枚举器,快速枚举
31.NSDictionary字典
关键字及其定义的集合。
32.NSNumber包装(以对象形式实现)基本数据类型
装箱:将一个基本类型的数据包装成对象。
取消装箱:从对象中提取基本类型的数据。
objective-c不支持自动装箱。
33.NSValue是NSNumber的父类。
+(NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;
传递的参数是你想要包装的数值的地址(如一个NSSize或你自己的struct)。通常,得到的是你想要存储的变量的地址(在C语言中使用操作 符&)。你也可以提供一个用来描述这个数据类型的字符串,通常用来说明struct中实体的类型和大小。你不用自己写代码来生成这个字符 串,@encode编译器指令可以接受数据类型的名称并为你生成合适的字符串。
NSRect rect = NSMakeRect(1, 2, 30, 40);
NSValue *value;
value = [NSValue valueWithBytes:&rect objCType:@encode(NSRect)];
[array addObject:value];
34.NSNull只有一个方法[NSNull null];
[NSNull null]总是返回一样的数值,所以你可以使用运算符==将该值与其他值进行比较。
35.单实例架构:只需要一个实例。
查找文件:
例如:NSFileManager *manager;
manager = [NSFileManager defaultManager];
defaultManager可以为我们创建一个属于我们自己的NSFileManger对象。
NSString *home = [@"~" stringByExpandingTildeInPath];将~替换成当前用户的主目录。
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:home];返回一个 NSDictionaryEnumerator,它是NSEnumerator的子类。每次在这个枚举器对象中调用nextObject时,都会返回该目 录中得一个文件的另一个路径。
36.内存管理
1)对象生命周期
对象的生命周期包括诞生(alloc或者new方法实现),生存(接受消息和执行操作),交友(借助方法的组合和参数)以及当他们的生命结束时最终死去(被释放)。当对象的生命周期结束时,他们的原材料(内存)将被回收以供新的对象使用。
2)引用计数
cocoa采用了一种称为引用计数的技术,有时候也叫保留计数。每个对象有一个与之相关联的整数,称作它的引用计数器或保留计数器。当某段代码需要 访问一 个对象时,该代码将该对象的保留计数器值+1,表示“我要访问该对象”。当这段代码结束对象访问时,将对象的保留计数器值-1,表示它不再访问该对象,当 保留计数器值为0时,表示不再有代码访问该对象了,因此该对象被销毁,其占用的内存被系统回收以便重用。
alloc,new,copy 1
retain +1
release -1
3)对象所有权
如果一个对象具有指向其他对象的实力变量,则称该对象拥有这些对象。
在类A中 B对象拥有其指向的C对象,则B对象拥有C对象。
如果一个函数创建了一个对象,则称该函数拥有它创建的这个对象。
main()函数创建了对象a 称main()函数拥有a对象
当多个实体拥有某个特定的对象时,对象的所有权关系就更加复杂了,这也是保留计数器值可能大于1的原因。
例子:
main() {
Engine *engine = [Engine new];
[car setEngine:engine];
}
现在哪个实体拥有engine对象?是main函数还是car类?哪个实体负责确保当engine对象不再被使用时能够收到release消息?因 为 car类正在使用engine对象,所以不可能是main函数。因为main函数随后还可能会使用engine对象,所以也不可能是car类。
解决方法是让car类保留engine对象,将engine对象的保留计数器值增加到2。这是因为car类和main函数这2个实体都正在使用 engine对象。car类应该在setEngine:方法中保留engine对象,而main函数应该释放engine对象。然后,当car类完成其任 务时再释放engine对象(在其dealloc方法中),最后engine对象占用的资源被回收。
如果您使用名字以“alloc”或“new”开头或名字中包含“copy”的方法(例如alloc,newObject或mutableCopy) 创建了 一个对象,则您会获得该对象的所有权;或者如果您向一个对象发送了一条retain消息,则您也会获得该对象的所有权。
4)访问方法中得保留和释放
5)自动释放池NSAutoreleasePool
是一个存放实体的池(集合)。你可以用NSMutableArray来编写自己的自动释放池,以容纳对象并在dealloc方法中向池中得所有对象发送release消息。
autorelease
当给一个对象发送autorelease消息时,实际上是将该对象添加到NSAutoreleasePool中。当自动释放池被销毁时,会向该池中得所有对象发送release消息。
6)自动释放池的销毁时间
在我们一直使用的Foudation库工具中,创建和销毁自动释放池的方法非常明确:
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
...
[pool release];
创建一个自动释放池时,该池自动成为活动的池。释放该池时,其保留计数器值归0,然后该池被销毁。在销毁的过程中,该池释放其包含的所有对象。当使 用 Application Kit时,cocoa定期自动为你创建和销毁自动释放池。通常是在程序处理完当前事件以后执行这些操作。你可以使用任意多得自动 释放对象,当不再使用它们时,自动释放池将自动为你清理这些对象。
你可能已经在xcode自动生成代码中遇见过另一种销毁自动释放池中对象的方式:-drain方法。该方法只是清空自动释放池而不是销毁它。并且只适用于mac os x10.4以上的版本。
7)自动释放池的工作过程
我们在任何时候向一个对象发送autorelease消息,该对象都会呗添加到这个自动释放池中。被加入到自动释放池的对象的引用计数器值不会变 化。当自 动释放池被销毁时(向自动释放池发送release消息,自动释放池的引用计数器值变为0,调用自身的dealloc函数),会调用自身的dealloc 函数,会向池中得对象发送release消息。
别走开,下页更精彩!