Objective-C学习之旅(四)----内存管理2----retain点语法⼀一、retain属性的主要作⽤用
1、O-C内存管理和点语法 1>OC内存管理正常情况要使⽤用⼤大量的retain和relrese操作 2>点语法可以减少使⽤用retain和release的操作⼆二、@property(retain)编译器如何申明 编译器对于@property中的retain展开是不⼀一样的 主要是要释放上⼀一次的值,增加本次计数器 在dog.h中声明的:
@property(retain)Dog *dog;
展开后为:
-(void) setDog:(Dog *)aDog;
-(Dog *)dog;
三、@synthesize编译器如何实现展开
在dog.m中实现:
@synthesize dog=_dog;
展开后为:
-(void) setDog:(Dog *)aDog{
if(_dog!=aDog){
[_dog release]; _dog=[aDog retain]; }
}
-(Dog *)dog{
return _dog;
}
四、dealloc需要注意内容 dealloc必须要释放dog(retain) 在dog.m中
-(void) dealloc
{
self.dog=nil;
[super dealloc];
}
五、copy属性的主要作⽤用 copy属性是完全把对象重新拷⻉贝了⼀一份,计数器从新设置为1,和之前拷⻉贝的数据完全脱离关系。 @property(copy) NSString* str;
//表⽰示属性的getter函数
-(double) str
{
return str;
}
//表⽰示属性的setter函数
-(void) setStr:(NSString *)newStr {
str=[newStr copy];
}
六、assign,retain,copy
1、foo=value;//简单的引⽤用赋值
2、foo=[value retain];//引⽤用赋值,并且增加value的计数器 3、foo=[value copy];//将value复制了⼀一份给foo,复制后,foo和value就毫⽆无关系Person.h中的代码
#import <Foundation/Foundation.h> #import "Dog.h" @interface Person : NSObject {
Dog *_dog;}
//- (void) setDog:(Dog *)aDog; //- (Dog *) dog; @property (retain) Dog *dog;
@end
Person.m
#import "Person.h"
@implementation Person @synthesize dog = _dog; //- (void) setDog:(Dog *)aDog //{ // // // // // // } //} //- (Dog *) dog //{ // return _dog; //}
- (void) dealloc {
NSLog(@"person is dealloc");
if (aDog != _dog) {
[_dog release];
_dog = [aDog retain];//让_dog技术器 +1
// 把⼈人拥有的_dog释放
//[_dog release], _dog = nil;self.dog = nil;
//[self setDog:nil];
[super dealloc];
}
@end
Dog.h
#import <Foundation/Foundation.h>
@interface Dog : NSObject {
int _ID;}
@property int ID; @end
Dog.m
#import "Dog.h" @implementation Dog
@synthesize ID = _ID;
- (void) dealloc {
NSLog(@"dog %d is dealloc", _ID);
[super dealloc];
}
@end
main.m
#import <Foundation/Foundation.h> #import "Person.h" #import "Dog.h" // Person
// Dog.
// Person Dog.
int main (int argc, const char * argv[]) {
@autoreleasepool { NSLog(@"Hello, World!");
Dog *dog1 = [[Dog alloc] init]; [dog1 setID:1];
Dog *dog2 = [[Dog alloc] init]; [dog2 setID:2];
Person *xiaoLi = [[Person alloc] init]; [xiaoLi setDog:dog1];
[xiaoLi setDog:dog2];
[dog1 release];
[xiaoLi release];
[dog2 release];
#if 0 Dog *dog1 = [[Dog alloc] init];
[dog1 setID:1];
Person *xiaoLi = [[Person alloc] init];// ⼩小丽要遛狗
[xiaoLi setDog:dog1];
Person *xiaoWang = [[Person alloc] init]; [xiaoWang setDog:dog1];
NSLog(@"dog1 retain count is %ld", [dog1 retainCount]);
// dog1 retain count is 3
[dog1 release]; NSLog(@"dog1 retain count2 is %ld",
[dog1 retainCount]);
// dog1 retain count2 is 2
[xiaoWang release];
NSLog(@"dog1 retain count3 is %ld", [dog1 retainCount]);
// person is dealloc
// dog1 retain count3 is 1
[xiaoLi release];
// person is dealloc
// dog 1 is dealloc
#endif
}
return 0;}
---------------------------
对于我们.net开发⼈人员来说,.net为我们提供了⾃自动内存管理的机制,我们不需去关⼼心内存的管理。但是iphone开发中却是不能的。这篇⽂文章将简述⼀一下objective-c的内存管理机制和⽅方法和⼀一些特性。
⼿手动的进⾏行内存管理
Cocoa和Objective-C的类都是NSObject的⼦子类。NSObject中有⼏几个⽅方法进⾏行内存管理。alloc⽅方法为对象分配⼀一⽚片内存空间。dealloc⽅方法⽤用于释放对象的空间。但是在我们的代码中将永远都不会使⽤用dealloc⽅方法,因为运⾏行时会为你调⽤用此⽅方法释放内存空间。⽽而你需要做的只是引⽤用计数,稍后介绍什么是引⽤用计数。
除了alloc和dealloc,NSObject的还有retain和release⽅方法两个⽅方法⽤用于引⽤用计数。retain⽅方法给retainCount变量加1,release⽅方法给retainCount变量减1。当使⽤用alloc为对象分配⼀一⽚片内存空间的时候,retainCount会为1。在这个对象的⽣生命周期内,这个对象可能继续被其它变量引⽤用。但有新的变量指向这个对象的时候,你应该调⽤用retain⽅方法,这样运⾏行时才会知道有新的引⽤用指向了这个变量,在这个对象⽣生存期中拥有它的使⽤用权。这个被Objective-C开发⼈人员称之为“拥有”。例如:
Foo * myFooOne = [[Foo alloc] init]; //retaincount 为1Foo * myFooTwo = myFooOne; //myFooTwo 指向了这个对象//retaincount 仍然为1
[myFooTwo retain]; //调⽤用retain⽅方法,运⾏行时才知道myFooTwo指向了该对象,retaincount 为2上⾯面的代码中,myFooTwo通过调⽤用retain⽅方法,取得了Foo对象的拥有权。在这个对象的⽣生命周期中,会有很多变
量来指向和引⽤用它。指向这个对象的变量也可以通过release⽅方法来解除这种拥有权。release⽅方法将会告诉运⾏行时,我已经使⽤用完这个变量了,已经不需要它了,retainCount计数减1。
当对象的retainCount的计数⼤大于或者等于1的时候,运⾏行时会继续维持这个对象。当对象的retainCount为0的时候,运⾏行时会释放这个对象,并回收它占得内存空间。
下图展⽰示了⼀一个Foo对象的⽣生命周期。Foo对象⾸首先在内存中分配⼀一个内存空间,并且被myFooOne引⽤用。在这个时候Foo对象的retaincount为1。
Foo * myFooOne = [[Foo alloc] init];
第⼆二个引⽤用变量指向Foo对象,这个引⽤用变量接着调⽤用retain⽅方法,其实也是调⽤用Foo对象的retain⽅方法。Foo对象的retaincount变成2。
Foo * myFooTwo = myFooOne;
[myFooTwo retain];
接着当myFooOne引⽤用不需要的时候,通过调⽤用release⽅方法,解除与Foo对象的拥有权,Foo对象的retaincount变成1。
[myFooOne release];
但myFooTwo不在需要的时候,同样通过调⽤用release⽅方法,解除与Foo对象的拥有权,Foo对象的retaincount变成0。
内存泄露
我们经常会在⼀一个⽅方法中声明对象,看下⾯面这个例⼦子:
-(void) myMethod { //incorrect method NSString * myString = [[NSString alloc] init]; //retainCount = 1 Foo * myFoo = [[Foo alloc] initWithName:myString]; //retainCount = 1 NSLog(@"Foo's Name:%@", [myFoo getName]); }
这上⾯面这个⽅方法中,我们为myString 和myFoo分配了内存空间。⽅方法执⾏行结束之后,两个变量超出了作⽤用域的范围,所以不再有效。但是这个⽅方法并没有releases这两个对象。所以运⾏行时没有释放这两个变量占据的内存空间。除⾮非你的应⽤用程序结束,否则这两个变量占据的内存空间⼀一直都是不可⽤用的。我们把它称之为内存泄露。
为了防⽌止内存泄露。⽆无论什么时候,我们创建⼀一个对象,或者创建⼀一个对象的拷⻉贝,我们都必须通过release⽅方法释放。
-(void) myMethod { NSString * myString = [[NSString alloc] init]; //retainCount=1
Foo * myFoo = [[Foo alloc] initWithName:myString]; //retainCount=1 NSLog("Foo's Name:%@", [myFoo getName]); [myFoo release]; //retainCount=0 so deallocate [myString release]; //retainCount=0 so deallocate
}
弱引⽤用
看下⾯面的例⼦子:
-(void) myMethod { //an incorrect method Foo * myFooOne = [[Foo alloc] initWithName:@"James"]; //retainCount=1 Foo * myFooTwo = myFooOne; //retainCount still 1 [myFooOne release]; //retaincount=0 so deallocated NSLog("Name:%@", [myFooTwo printOutName]); //runtime error }
nyFooTwo指向了Foo对象,但是没有调⽤用retain⽅方法,就是⼀一种弱引⽤用,上⾯面的代码会在运⾏行时报错。因为myFooOne调⽤用release⽅方法。retaincount变成0,运⾏行时,回收了对象的内存空间。然后myFooTwo调⽤用printPutName⾃自然就报错了,⻅见下图说明。
总结:本⽂文简单的介绍了⼀一下⼿手动的进⾏行内存管理、内存泄露、弱引⽤用等objective-c的知识。
作者:朱祁林
出处:http://zhuqil.cnblogs.com本⽂文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在⽂文章⻚页⾯面明显位置给出原⽂文连接,否则保留追究法律责任的权利。
---------------------------
⽂文/OSChina
之前写过类似的⽂文章,这篇以做总结,希望能帮助刚上船的兄弟。_iPhone系统中的Objective-C的内存管理机制是⽐比较灵活的,即可以拿来像C/C++⼀一样⽤用,也可以加个AutoreleasePool让它升级为半⾃自动化的内存管理语⾔言。当然,也不能拿JAVA虚拟机中的全⾃自动化GC来⽐比?
引⽤用计数是实例对象的内存回收唯⼀一参考引⽤用计数(retainCount)是Objective-C管理对象引⽤用的唯⼀一依据。调⽤用实例的release⽅方法后,此属性减⼀一,减到为零时对象的dealloc⽅方法被⾃自动调⽤用,进⾏行内存回收操作,也就是说我们永不该⼿手动调⽤用对象的dealloc⽅方法。
它的内存管理API⽼老简单⽼老简单了,下⾯面就是它主要操作接⼝口:1,alloc, allocWithZone,new(带初始化)
为对象分配内存,retainCount为“1”,并返回此实例2,retain
retainCount 加“1”3,copy,mutableCopy
复制⼀一个实例,retainCount数为“1”,返回此实例。所得到的对象是与其它上下⽂文⽆无关的,独⽴立的对象(干净对象)。
4,release
retainCount 减“1”,减到“0”时调⽤用此对象的dealloc⽅方法
5,autorelease在当前上下⽂文的AutoreleasePool栈顶的autoreleasePool实例添加此对象,由于它的引⼊入使Objective-C(⾮非GC管理环境)由全⼿手动内存管理上升到半⾃自动化。
Objective-C内存管理准则我们可以把上⾯面的接⼝口按对retainCount的操作性质归为两类,A类是加⼀一操作:1,2,3B类是减⼀一操作:4,5(延时释放)
内存管理准则如下:
1,A与B类的调⽤用次数保持⼀一制2,为了很好的保障准则⼀一,以实例对象为单位,谁A了就谁B,没有第⼆二者参与
例:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]
init];
NSObject *o = [[NSObject alloc] init];
1
//retainCount为
[o retain]; //retainCount为2
[o release]; //retainCount为1
[o autorelease]; //retainCount为1
[pool release]; //retaincount为0,触发dealloc⽅方法
对象的拥有者⾯面向对象领域⾥里有个引⽤用的概念,区别于继承,引⽤用常被⽤用来当做偶合性更⼩小的设计。继承是强依赖,对吧。我们要降偶软件的设计,就要尽量减少对它的使⽤用。但没有任何偶合的模块或功能是没有⽤用的?对吧,那我们只能多⽤用引⽤用了吧。⼀一个实例拥有另⼀一个实例的时候,我们称它为引⽤用了另⼀一个实例。
⽐比如ClassA类的⼀一个属性对象的Setter⽅方法:
- ( void )setMyArray:(NSMutableArray *)newArray
{
if (myArray != newArray) {
[myArray release];
myArray = [newArray retain];
}}
假设这个类的⼀一个实例为'a',调⽤用setMyArray后,我们就可以说a拥有了⼀一个新的myArray实例,也可以说a引⽤用了⼀一个新的myArray实例。其中调⽤用的retain⽅方法,使myArray的retainCount加⼀一,我们需要注意以下两个地⽅方:1,setMyarray⽅方法中,在retain之前先release了旧实例⼀一次2,在本实例的dealloc⽅方法中,本应该是要再次release当前实例的,但回头看看参考内存管理准则。它并不合理,对吧。。。多了⼀一次release。这⾥里⽐比较推荐的做法是:
[ myArray setMyArray:nil ];这样可以巧妙的使当前实例release⽽而不出错(我们可以向nil发送消息?其实它本⾝身就是个整数0),并符合我们的内存管理准则。更主要的是,很简单,你不需要考虑过多的事情。
另外⼀一个⽐比较容易忽略⽽而⼜又⽐比较经典的问题是实例变量的循环引⽤用,Objective-C为此区分了,其实也相当相当的简单:
1,强引⽤用,上⾯面讲的就是强引⽤用,存在retainCount加⼀一。2,弱引⽤用,但凡是assign声明并直接⽤用指针赋值实现的被称之为弱引⽤用,不存在retainCount加⼀一的情况。
AutoreleasePool使Objective-C成为内存管理半⾃自动化语⾔言如果仅仅是上⾯面这些,很简单,对吧。但往往很多⼈人都会迷糊在⾃自动内存管理这块上,感觉像是有魔法,但其实原理也很简单?
先看看最经典的程序⼊入⼝口程序:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]
init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
我们先把pool看成⼀一个普通对象?很简单,先是alloc,pool的retainCount为1。第三句release,retainCount为0,⾃自动调⽤用它的dealloc⽅方法。它和任何其它普通对象没 任何区别。
魔法在哪⾥里?在声明pool后,release它之前的这段代码,所有段⾥里的代码(先假设中间没有声明其它的AutoreleasePool实例),凡是调⽤用了autorelase⽅方法的实例,都会把它的retainCount加1,并在此pool实例中添1次此实例要回收的记录以做备案。当此pool实例dealloc时,⾸首先会检查之前备案的所有实例,所有记录在案的实例都会依次调⽤用它的release⽅方法。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];NSObject *o = [[NSObject alloc] init];//在pool实例dealloc时,release⼀一次此实例,重要的是并不是在此⾏行去release[o autorelease];//此时还可以看到我们的o实例还是可⽤用的,并且retainCount为1
NSLog(@ "o retainCount:%d" ,[o retainCount]);
//pool 的 retainCount为0,⾃自动调⽤用其dealloc⽅方法,我们之前备案的⼩小o也将在这⾥里release⼀一次
[pool release];
真对同⼀一个实例,同⼀一个Pool是可以多次注册备案(autorelease)的。在⼀一些很少的情况化可能会出现这种需求:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]
init];
NSObject *o = [[NSObject alloc] init];
[o retain];
[o autorelease];
[o autorelease];
[pool release];
我们调⽤用了两次A类(retainCount加1的⽅方法),使其retainCount为2,⽽而接下来的两次autorelease⽅方法调⽤用,使其在pool中注册备案了两次。这⾥里的pool将会在回收时调⽤用此实例的两次release⽅方法。使其retainCount降为0,完成回收内存的操作,其实这也是完全按照内存管理规则办事的好处?
AutoreleasePool是被嵌套的!池是被嵌套的,嵌套的结果是个栈,同⼀一线程只有当前栈顶pool实例是可⽤用的:
栈顶 |
pool_5 |
栈中 |
pool_4 |
栈中 |
pool_3 |
栈中 |
pool_2 |
栈底 |
pool_1 |
其代码如下:
NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc]
init];
NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc]
init];
NSAutoreleasePool *pool3 = [[NSAutoreleasePool alloc]
init];
NSObject *o = [[NSObject alloc] init] autorelease];
[pool3 release];
[pool2 release];
[pool1 release];
我们可以看到其栈顶是pool3,o的autorelease是把当前的release放在栈顶的pool实例管理。。。也就是pool3。在⽣生命周期短,产⽣生⼤大量放在autoreleasePool中管理实例的情况下经常⽤用此⽅方法减少内存使⽤用,达到内存及时回收的⺫⽬目的。
AutoreleasePool还被⽤用在哪⾥里?在上⾯面的例⼦子⾥里,也可以看到,我们在执⾏行autorelease⽅方法时,并没有时时的进⾏行release操作?它的release被延时到pool实例的dealloc⽅方法⾥里。这个⼩小细节使我们的Objective-C⽤用起来可以在⽅方法栈中申请堆中的内存,创建实例,并把它放在当前pool中延迟到此⽅方法的调⽤用者释放?
---------------------
如果你是Objective-C的新⼿手,肯定会欢迎ARC。⼀一开始,有太多的东⻄西要学习,有了ARC就不⽤用担⼼心⼿手⼯工计数的问题。随着你开始接触⼀一些已有的库,就会经历⼀一些痛苦(译者注: ⺫⽬目前的第三⽅方库很多不兼容ARC),但只要习惯了将它们排除在ARC之外,就没什么问题了。如果你不是新⼿手,在没有ARC的年代已经玩的很high,那么可能会觉得“我干嘛要⽤用它!”对你来说,这可能是正确的答案——就⺫⽬目前⽽而⾔言。因为,⼤大多数流⾏行的库都还没转到ARC下,⽽而且ARC对Core Foundation的⽀支持也不好。使⽤用CF类的时候有⼀一些限制,⽽而且,移植代码的时候,为了让免费桥接⽣生效,还需要加上⼀一⼤大堆限定符。
在我看来,⺫⽬目前ARC已经是可以使⽤用的状态了。不过,除⾮非你对它很熟悉,否则还是先⽤用在新⼯工程⾥里会⽐比较好。虽然ARC兼容iOS 4.0以上,但弱引⽤用只在iOS 5.0以上才⽀支持,所以现在还是不要把所有东⻄西都移植过去(有⼀一些相关的⽂文章,请参考最后的“资源”⼀一节)⾄至于性能⽅方⾯面,早前有报告指出⽤用ARC之后速度会变快,我想可能是由于减少对⾃自动释放的依赖的缘故。虽然这完全可以通过改进代码、更好地使⽤用retain/release来实现,但我觉得重点在于,ARC总是会⾃自动帮你选择最优的⽅方法。
⺫⽬目前为⽌止,还是有很多令⼈人苦恼的东⻄西被移到ARC,但还不是全部。在⻓长周末后我们会离开⼀一段时间以投⼊入到新的项⺫⽬目,但是这是苹果⼀一个“新的”推荐⼿手段,所以你可以肯定以后的设计决策会继续强⼤大ARC并且让⼤大家离开使⽤用⼿手动的引⽤用计数
呵呵,这个是我在⺴⽹网上查到的,你⾃自⼰己看看把关于ARC你可以在这个⺴⽹网址⾥里看看http://www.oschina.net/translate/objc-automatic-reference-counting-in-xcode-explained
------------
Objective-C学习之旅(三)----内存管理1--点语法1、点语法及其好处
1、⽅方便程序员能够很快的转到O-C上来 2、让程序设计简单化 3、隐藏了内存管理细节 4、隐藏了多线程、同步、加锁细 节 5、点语法的使⽤用
Dog *dog=[[Dog aloc] init];
[dog setAge:100];
int dogAge=[dog age];
NSLog(@"Dog Age is %d",dogAge);
下⾯面的代码⽤用点语法
dog.age=200;//调⽤用setAge⽅方法
dogAge=dog.age;//调⽤用age⽅方法
这⾥里的点不上调⽤用的dog这个对象的字段,⽽而且在调⽤用⽅方法。dog.age是在调⽤用setAge这个⽅方法,下⾯面的dog.age 是在调⽤用age这个⽅方法。
点语法是编译器级别编译器会把dog.age=200;展开成[dog setAge:200];会把dogAge=dog.age;展开成[dog age];函数调⽤用 6、点语法setter和getter规范 setter函数展开规范
dog.age=200;
[dog setAge:200];
getter函数展开规范
int dogAge=dog.age;
int dogAge=[dog age];
项⺫⽬目当中如果想⽤用点语法,必须在项⺫⽬目中的.h⽂文件和.m⽂文件中声明和实现setAge和age⽅方法,也就是说要声明和实现getter和setter⽅方法。
2、@property @synthesize如何使⽤用 @property是让编译器⾃自动产⽣生函数申明 不再写下⾯面2⾏行代码
-(void) setAge:(int)newAge;
-(void) age;
只需要下列⼀一⾏行就可以代替
@property int age;
@synthesize 意思是合成 @synthesize就是编译器⾃自动实现getter和setter函数 不⽤用写下列代码
- (void) setAge:(int)newAge {
age=newAge;
}
-(int) age { return age; }
只需要些
@synthesize age;
3、@property @synthesize编译器如何展开
@property @synthesize只能展开成标准的模板,如果想在getter和setter函数中增加内容,则不能⽤用@synthesize表⽰示⽅方法。4、如何使⽤用点语法 self.age放在=号的左边和右边是不⼀一样的,放在左边是表⽰示调⽤用setter函数,放在右边表⽰示调⽤用getter函数。 为了区别开age,我们会对dog类做⼀一些改动
@interface Dog:NSObject
{
int _age;//改动了以前是int age;
}
@property int age;
@end;
#import "Dog.h"
@implementation Dog
@synthesize age=_age;
@end
5、@property其他属性 readwrite(缺省),readonly
表⽰示属性是可读写的,也就是说可以使⽤用getter和setter,⽽而readonly只能使⽤用getter assign(缺省),retain,copy
表⽰示属性如何存储
nonatomic
表⽰示不⽤用考虑线程安全问题 getter=......,setter=...... 重新设置getter函数和setter函数名这个项⺫⽬目的代码如下;
dog.h⽂文件代码
#import <Foundation/Foundation.h>
@interface Dog : NSObject {
int _age;}
//setter and getter function //- (void) setAge:(int)newAge; //- (int) age; @property int age; @end
dog.m⽂文件代码
#import "Dog.h"
@implementation Dog @synthesize age=_age; //- (void) setAge:(int)newAge //{ // age=newAge; //} //- (int) age //{ // return age; //} @end
main.m⽂文件代码
#import <Foundation/Foundation.h> #import "Dog.h"
int main (int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!"); Dog *dog=[[Dog alloc] init];
[dog setAge:300]; int dogAge=[dog age]; NSLog(@"dog age is %d", dogAge); //classic mode
Dog *dog1=[[Dog alloc] init]; dog1.age=400; //[dog1 setAge:200]; dogAge=dog1.age; //dogAge=[dog age]; NSLog(@"dog1 age is %d", dogAge);
}
return 0;}