- 浏览: 711370 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
javenwong:
你好,我现在需要给一个Composite做圆角处理,不知道怎么 ...
SWT圆角窗口绘制 -
whao189:
回家了详细看看
Storyboard 解析 -
zhaoyubetter:
确实够麻烦
IOS 开发环境,证书和授权文件是什么? -
datawarehouse:
一直对这两个认为是一样的,现在学习了。
Oracle DB中的Schema -
yulanfeiyang:
顶,mark
下载音频,在IOS设备上播放
1. 内总管理原则(引用计数)
IOS的对象都继承于NSObject, 该对象有一个方法:retainCount ,内存引用计数。 引用计数在很多技术都用到: window下的COM组件,多线程的信号量,读写锁,思想都一样。
(一般情况下: 后面会讨论例外情况)
alloc 对象分配后引用计数为1
retain 对象的引用计数+1
copy copy 一个对象变成新的对象(新内存地址) 引用计数为1 原来对象计数不变
release 对象引用计数-1 如果为0释放内存
autorelease 对象引用计数-1 如果为0不马上释放,最近一个个pool时释放
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
[sMessage
retain]; //2
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
内存管理的原则就是最终的引用计数要平衡,
如果最后引用计数大于0 则会内存泄露
如果引用 计数等于0还对该对象进行操作,则会出现内存访问失败,crash 所以尽量设置为nil
这两个问题都很严重,所以请一定注意内存释放和不用过后设置为nil
2. autoReleasePool
每个工程都有一个 main.m 文件: 内容如下:
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
很明显C语言的main 函数:
NSAutoreleasePool 是用来做autorelease 变量释放的,前面说了, autorelease不会马上释放,当他到了最近的pool release 时会检查reatin count 是不是为0, 为0就释放。
当我们在一段代码时加入了大量的autorelease变量时,我们应该为这段代码加上
Autoreleasepool,其它时候不用
在返回一个对象时常常要返回一个autorelease 对象,因为objectC 对象都是动态内存,没有
栈的概念,所以不能像C/C++一样返回一个对象到栈,只能用autorelease对象。
3. 成员变量与属性
实际情况并非上面那么简单,你可能需要在一个函数里调用另一个函数分配的变量这时候
有两个选择: 类成员变量和使用属性
@interface TestMem: NSObject {
TestObject *m_testObject ; // 成员变量
TestObject *testObject; //成员变量
}
成员变量与上面的内存管理是一致的,只是在不同的函数里要保持引用计数加减的平衡
所以要你要每次分配的时候检查是否上次已经分配了。是否还能调用
什么时候用属性?
1. 把成员做为public.
2. outlet 一般声明为属性( 这个内存于系统控制,但我们还是应该做一样操作,后面会讲)
3. 如果很多函数都需要改变这个对象 ,或这个函数会触发很多次,建议使用属性。我们看看属性函数展开后是什么样子:
// assign
-(void)setTestObjectid)newValue{
testObject= newValue;
}
// retain
-(void)setTestObject id)newValue{
if (testObject!= newValue) {
[testObject release];
testObject= [newValue retain];
}
}
// copy
-(void)setTestObjectid)newValue{
if (testObject != newValue) {
[testObject release];
testObject = [newValue copy];
}
}
asssign 相于于指针赋值,不对引用计数进行操作,注意原对象不用了,一定要把这个设置为nil
retain 相当于对原对象的引用计数加1
copy 不对原对象的引用计数改变,生成一个新对象引用计数为1
注意:
self.testObject 左值调用的是setTestObject 方法. 右值为get方法,get 方法比较简单不用说了
而 真接testObject 使用的是成员变量
self.testObject = [[testObject alloc] init]; // 错 reatin 两次
testObject = [NSArray objectbyindex:0]; //错 不安全,没有retain 后面release会出错
如果testObject已有值也会mem leak
4. 自动管理对象
IOS 提供了很多static(+) 创建对象的类方法,这些方面是静态的,可以直接用类名
调用如:
NSString *testString = [NSString stringWithFormat:@"test" ];
testString 是自动管理的对象,你不用relese 他,他有一个很大的retain count, release后数字不变。
5. 例外
有一些通过alloc 生成的对象相同是自动管理的如:
NSString *testString = [[NSString alloc] initWithString:@"test1"];
retain count 同样是很大的数,没办法release
但为了代码对应,还是应该加上[ testString release];
不然xcode的Analyze 会认识内存leak, 但Instruments leak 工具检测是没有的
6.view 内存管理
通常我们编程会用到view, 在view 中的方法:
viewDidload
didReceiveMemoryWarning
viewDidUnload
@property (retain) NSArray *iarrTestMem
viewDidLoad
init retain is 0
(alloc) +1 =1
if(memwarning)
didrecivememwarning -0 =1
didviewunload -1
=0
will load viewDidLoad when next load view
esle
dealloc: -1
=0
我们来看一个变量的生命周期
当view被alloc
A. 框架会自动调用viewDidLoad
一般来说在viewDidLoad 中分配变量:
假设为alloc 现在变量的reatin 为 0 +1 = 1;
第一种情况:
B. 如果这时候view被释放,会调用 dealloc,
这时候我们应该在dealloc里release 这个变量现在为0
第二种情况:
B. 这时候view被换到下层,如navigation的上级,不可显示状态:如果
系统内存吃紧: 系统会发 消息,我们的didrecivememwarning 函数
被调用 ,该函数是提醒我们应该释放一些现在用不上的东西,特别是一些较大的
如图片,cache 数据等, 注意如果在这里释放了,代码在用的地方要进行
判断,是否需要重新加载
C. 他会调用didviewunload
这时候我们要注意了,因为他调用了didviewunload,在下次这个view被显示的时候
会再次调用didviewload, 我们在didviewload里分配的娈童就会被分配两次,所以我
们在这里一定要释放didview里分配的变量,不管直接分配的还是间接分配的,
如发消息给其它函数分配的。 那我们在下次分配之前释放一次行不行, 不行,因为这
时候内存吃紧了,你应该多释放内存,不然你的程序可能会被系统kill. 还有虽然对
[xxx release] 对nil 发 release 是没问题的,但逻辑上让人觉得很奇怪,以为在别的
地方分配过。 所以这里应该释放内存,
如果你是一个属性,用:
self.xxx = nil 比较好,他相当于帮你释放了原来的,还把xxx设置为了nil.
如果不是一个属性 :也最好把xxx= nil. 这样比较安全。如果这个view不再被换入
下一步直接调用 dealloc, 这时候你的dealloc里的[xxx release], 如果这里的xxx不为nil你在
didviewunload里release 了,就非常危险
D. 如果这时候view被释放,会调用 dealloc,
这时候我们应该在dealloc里release
前面我们说了outlet的内存没办法管理(看下生命周期)
@property (nonatomic,retain) IBOutlet UILabel *ilblTestMem;
init retain is 2
didviewLoad:
= 2
if(memwarning)
memwaring - retain count -1
= 1
viewdidunload: -1
=0
will load viewDidLoad when next load view
else
dealloc: -1
=1
第一种情况
A. didviewLoad:
retain count 为2
B. dealloc:
-1 retain count 为1
第二种情况:
B. memwarning
系统会把 retain count 减到1
C. viewdidunload
我们应该release 一次 这样 retain count 到0,变量会马上dealloc,更快的释放内存
注意用 self.ilblTestMem = nil 设置为nil, 为 dealloc 做准备
D. dealloc:
-1
NSObject *sMessage = [[NSObjectalloc]init];
//1
[sMessagerelease]; //1
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
[sMessagerelease]; //0
//not crash (retainCount also can call)
//crash (can not call)
[sMessagerelease];
@property (nonatomic, retain) TestObject testObject*; //为testObject成员变量生成属性方法
@end
iPhone/Mac Objective-C内存管理教程和原理剖析
前言
初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃。我在这里总结了自己对objective-C内存管理机制的研究成果和经验,写了这么一个由浅入深的教程。希望对大家有所帮助,也欢迎大家一起探讨。
此文涉及的内存管理是针对于继承于NSObject的Class。
一 基本原理
Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。
1 Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。
ClassA *obj1 = [[ClassA alloc] init];
2 Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。
[obj1 dealloc];
这带来了一个问题。下面代码中obj2是否需要调用dealloc?
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello]; //输出hello
[obj1 dealloc];
[obj2 hello]; //能够执行这一行和下一行吗?
[obj2 dealloc];
不能,因为obj1和obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello]和[obj2 dealloc]。obj2实际上是个无效指针。
如何避免无效指针?请看下一条。
3 Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。
ClassA *obj1 = [[ClassA alloc] init]; //对象生成时,retain count = 1
[obj1 release]; //release使retain count减1,retain count = 0,dealloc自动被调用,对象被销毁
我们回头看看刚刚那个无效指针的问题,把dealloc改成release解决了吗?
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 0,对象被销毁
[obj2 hello];
[obj2 release];
[obj1 release]之后,obj2依然是个无效指针。问题依然没有解决。解决方法见下一条。
4 Objective-C指针赋值时,retain count不会自动增加,需要手动retain。
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 2 – 1 = 1
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 0,对象被销毁
问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。(1-4可以参考附件中的示例程序memman-no-pool.m)
这样的确不会内存泄露,但似乎有点麻烦,有没有简单点的方法?见下一条。
5 Objective-C中引入了autorelease pool(自动释放对象池),在遵守一些规则的情况下,可以自动释放对象。(autorelease pool依然不是.Net/Java那种全自动的垃圾回收机制)
5.1 新生成的对象,只要调用autorelease就行了,无需再调用release!
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release
5.2 对于存在指针赋值的情况,代码与前面类似。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
//对于obj1,无需调用(实际上不能调用)release
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 2-1 = 1
细心的读者肯定能发现这个对象没有被销毁,何时销毁呢?谁去销毁它?(可以参考附件中的示例程序memman-with-pool.m)请看下一条。
6 autorelease pool原理剖析。(其实很简单的,一定要坚持看下去,否则还是不能理解Objective-C的内存管理机制。)
6.1 autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6.2 NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中
6.3 NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。
6.4 默认只有一个autorelease pool,通常类似于下面这个例子。
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
// do something
[pool release];
return (0);
} // main
所有标记为autorelease的对象都只有在这个pool销毁时才被销毁。如果你有大量的对象标记为autorelease,这显然不能很好的利用内存,在iphone这种内存受限的程序中是很容易造成内存不足的。例如:
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
for (j = 0; j < 100000; j++ )
[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
}
[pool release];
return (0);
} // main
(可以参考附件中的示例程序memman-many-objs-one-pool.m,运行时通过监控工具可以发现使用的内存在急剧增加,直到pool销毁时才被释放)你需要考虑下一条。
7 Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyytt和neogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
for (j = 0; j < 100000; j++ )
[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
[loopPool release];
}
[pool release];
return (0);
} // main
二 口诀与范式
1 口诀。
1.1 谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过alloc、new或copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用release或autorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。
1.2 除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。
1.3 谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。不知道为什么苹果公司的文档没有强调这个非常重要的一点,请参考范式2.7和第三章。
2 范式。
范式就是模板,就是依葫芦画瓢。由于不同人有不同的理解和习惯,我总结的范式不一定适合所有人,但我能保证照着这样做不会出问题。
2.1 创建一个对象。
ClassA *obj1 = [[ClassA alloc] init];
2.2 创建一个autorelease的对象。
ClassA *obj1 = [[[ClassA alloc] init] autorelease];
2.3 Release一个对象后,立即把指针清空。(顺便说一句,release一个空指针是合法的,但不会发生任何事情)
[obj1 release];
obj1 = nil;
2.4 指针赋值给另一个指针。
ClassA *obj2 = obj1;
[obj2 retain];
//do something
[obj2 release];
obj2 = nil;
2.5 在一个函数中创建并返回对象,需要把这个对象设置为autorelease
ClassA *Func1()
{
ClassA *obj = [[[ClassA alloc]init]autorelease];
return obj;
}
2.6 在子类的dealloc方法中调用基类的dealloc方法
-(void) dealloc
{
…
[super dealloc];
}
2.7 在一个class中创建和使用property。
2.7.1 声明一个成员变量。
ClassB *objB;
2.7.2 声明property,加上retain参数。
@property (retain) ClassB* objB;
2.7.3 定义property。(property的默认实现请看第三章)
@synthesize objB;
2.7.4 除了dealloc方法以外,始终用.操作符的方式来调用property。
self.objB 或者objA.objB
2.7.5 在dealloc方法中release这个成员变量。
[objB release];
示例代码如下(详细代码请参考附件中的memman-property.m,你需要特别留意对象是在何时被销毁的。):
@interface ClassA : NSObject
{
ClassB* objB;
}
@property (retain) ClassB* objB;
@end
@implementation ClassA
@synthesize objB;
-(void) dealloc
{
[objB release];
[super dealloc];
}
@end
2.7.6 给这个property赋值时,有手动release和autorelease两种方式。
void funcNoAutorelease()
{
ClassB *objB1 = [[ClassB alloc]init];
ClassA *objA = [[ClassA alloc]init];
objA.objB = objB1;
[objB1 release];
[objA release];
}
void funcAutorelease()
{
ClassB *objB1 = [[[ClassB alloc]init] autorelease];
ClassA *objA = [[[ClassA alloc]init] autorelease];
objA.objB = objB1;
}
三 @property (retain)和@synthesize的默认实现
在这里解释一下@property (retain) ClassB* objB;和@synthesize objB;背后到底发生了什么(retain property的默认实现)。property实际上是getter和setter,针对有retain参数的property,背后的实现如下(请参考附件中的memman-getter-setter.m,你会发现,结果和memman-property.m一样):
@interface ClassA : NSObject
{
ClassB *objB;
}
-(ClassB *) getObjB;
-(void) setObjB:(ClassB *) value;
@end
@implementation ClassA
-(ClassB*) getObjB
{
return objB;
}
-(void) setObjB:(ClassB*) value
{
if (objB != value)
{
[objB release];
objB = [value retain];
}
}
在setObjB中,如果新设定的值和原值不同的话,必须要把原值对象release一次,这样才能保证retain count是正确的。
由于我们在class内部retain了一次(虽然是默认实现的),所以我们要在dealloc方法中release这个成员变量。
-(void) dealloc
{
[objB release];
[super dealloc];
}
四 系统自动创建新的autorelease pool
在生成新的Run Loop的时候,系统会自动创建新的autorelease pool(非常感谢网友hhyytt和neogui的提醒)。注意,此处不同于xcode在新建项目时自动生成的代码中加入的autorelease pool,xcode生成的代码可以被删除,但系统自动创建的新的autorelease pool是无法删除的(对于无Garbage Collection的环境来说)。Objective-C没有给出实现代码,官方文档也没有说明,但我们可以通过小程序来证明。
在这个小程序中,我们先生成了一个autorelease pool,然后生成一个autorelease的ClassA的实例,再在一个新的run loop中生成一个autorelease的ClassB的对象(注意,我们并没有手动在新run loop中生成autorelease pool)。精简的示例代码如下,详细代码请见附件中的memman-run-loop-with-pool.m。
int main(int argc, char**argv)
{
NSLog(@"create an autorelasePool\n");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"create an instance of ClassA and autorelease\n");
ClassA *obj1 = [[[ClassA alloc] init] autorelease];
NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:0.0
target:obj1
selector:@selector(createClassB)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
[now release];
[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例
NSLog(@"releasing autorelasePool\n");
[pool release];
NSLog(@"autorelasePool is released\n");
return 0;
}
输出如下:
create an autorelasePool
create an instance of ClassA and autorelease
create an instance of ClassB and autorelease
ClassB destroyed
releasing autorelasePool
ClassA destroyed
autorelasePool is released
注意在我们销毁autorelease pool之前,ClassB的autorelease实例就已经被销毁了。
有人可能会说,这并不能说明新的run loop自动生成了一个新的autorelease pool,说不定还只是用了老的autorelease pool,只不过后来drain了一次而已。我们可以在main函数中不生成autorelease pool。精简的示例代码如下,详细代码请见附件中的memman-run-loop-without-pool.m。
int main(int argc, char**argv)
{
NSLog(@"No autorelasePool created\n");
NSLog(@"create an instance of ClassA\n");
ClassA *obj1 = [[ClassA alloc] init];
NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:0.0
target:obj1
selector:@selector(createClassB)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
[now release];
[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例
NSLog(@"Manually release the instance of ClassA\n");
[obj1 release];
return 0;
}
输出如下:
No autorelasePool created
create an instance of ClassA
create an instance of ClassB and autorelease
ClassB destroyed
Manually release the instance of ClassA
ClassA destroyed
我们可以看出来,我们并没有创建任何autorelease pool,可是ClassB的实例依然被自动销毁了,这说明新的run loop自动创建了一个autorelease pool,这个pool在新的run loop结束的时候会销毁自己(并自动release所包含的对象)。
补充说明
在研究retain count的时候,我不建议用NSString。因为在下面的语句中,
NSString *str1 = @”constant string”;
str1的retain count是个很大的数字。Objective-C对常量字符串做了特殊处理。
当然,如果你这样创建NSString,得到的retain count依然为1
NSString *str2 = [NSString stringWithFormat:@”123”];
涉及的示例程序代码(已去除隐藏,觉得有用的话请顶一下此文):http://files.cnblogs.com/VinceYuan/objective-c-memman.zip
http://blog.csdn.net/weiqubo/article/details/7376189
IOS的对象都继承于NSObject, 该对象有一个方法:retainCount ,内存引用计数。 引用计数在很多技术都用到: window下的COM组件,多线程的信号量,读写锁,思想都一样。
(一般情况下: 后面会讨论例外情况)
alloc 对象分配后引用计数为1
retain 对象的引用计数+1
copy copy 一个对象变成新的对象(新内存地址) 引用计数为1 原来对象计数不变
release 对象引用计数-1 如果为0释放内存
autorelease 对象引用计数-1 如果为0不马上释放,最近一个个pool时释放
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
[sMessage
retain]; //2
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
内存管理的原则就是最终的引用计数要平衡,
如果最后引用计数大于0 则会内存泄露
如果引用 计数等于0还对该对象进行操作,则会出现内存访问失败,crash 所以尽量设置为nil
这两个问题都很严重,所以请一定注意内存释放和不用过后设置为nil
2. autoReleasePool
每个工程都有一个 main.m 文件: 内容如下:
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
很明显C语言的main 函数:
NSAutoreleasePool 是用来做autorelease 变量释放的,前面说了, autorelease不会马上释放,当他到了最近的pool release 时会检查reatin count 是不是为0, 为0就释放。
当我们在一段代码时加入了大量的autorelease变量时,我们应该为这段代码加上
Autoreleasepool,其它时候不用
在返回一个对象时常常要返回一个autorelease 对象,因为objectC 对象都是动态内存,没有
栈的概念,所以不能像C/C++一样返回一个对象到栈,只能用autorelease对象。
3. 成员变量与属性
实际情况并非上面那么简单,你可能需要在一个函数里调用另一个函数分配的变量这时候
有两个选择: 类成员变量和使用属性
@interface TestMem: NSObject {
TestObject *m_testObject ; // 成员变量
TestObject *testObject; //成员变量
}
成员变量与上面的内存管理是一致的,只是在不同的函数里要保持引用计数加减的平衡
所以要你要每次分配的时候检查是否上次已经分配了。是否还能调用
什么时候用属性?
1. 把成员做为public.
2. outlet 一般声明为属性( 这个内存于系统控制,但我们还是应该做一样操作,后面会讲)
3. 如果很多函数都需要改变这个对象 ,或这个函数会触发很多次,建议使用属性。我们看看属性函数展开后是什么样子:
// assign
-(void)setTestObjectid)newValue{
testObject= newValue;
}
// retain
-(void)setTestObject id)newValue{
if (testObject!= newValue) {
[testObject release];
testObject= [newValue retain];
}
}
// copy
-(void)setTestObjectid)newValue{
if (testObject != newValue) {
[testObject release];
testObject = [newValue copy];
}
}
asssign 相于于指针赋值,不对引用计数进行操作,注意原对象不用了,一定要把这个设置为nil
retain 相当于对原对象的引用计数加1
copy 不对原对象的引用计数改变,生成一个新对象引用计数为1
注意:
self.testObject 左值调用的是setTestObject 方法. 右值为get方法,get 方法比较简单不用说了
而 真接testObject 使用的是成员变量
self.testObject = [[testObject alloc] init]; // 错 reatin 两次
testObject = [NSArray objectbyindex:0]; //错 不安全,没有retain 后面release会出错
如果testObject已有值也会mem leak
4. 自动管理对象
IOS 提供了很多static(+) 创建对象的类方法,这些方面是静态的,可以直接用类名
调用如:
NSString *testString = [NSString stringWithFormat:@"test" ];
testString 是自动管理的对象,你不用relese 他,他有一个很大的retain count, release后数字不变。
5. 例外
有一些通过alloc 生成的对象相同是自动管理的如:
NSString *testString = [[NSString alloc] initWithString:@"test1"];
retain count 同样是很大的数,没办法release
但为了代码对应,还是应该加上[ testString release];
不然xcode的Analyze 会认识内存leak, 但Instruments leak 工具检测是没有的
6.view 内存管理
通常我们编程会用到view, 在view 中的方法:
viewDidload
didReceiveMemoryWarning
viewDidUnload
@property (retain) NSArray *iarrTestMem
viewDidLoad
init retain is 0
(alloc) +1 =1
if(memwarning)
didrecivememwarning -0 =1
didviewunload -1
=0
will load viewDidLoad when next load view
esle
dealloc: -1
=0
我们来看一个变量的生命周期
当view被alloc
A. 框架会自动调用viewDidLoad
一般来说在viewDidLoad 中分配变量:
假设为alloc 现在变量的reatin 为 0 +1 = 1;
第一种情况:
B. 如果这时候view被释放,会调用 dealloc,
这时候我们应该在dealloc里release 这个变量现在为0
第二种情况:
B. 这时候view被换到下层,如navigation的上级,不可显示状态:如果
系统内存吃紧: 系统会发 消息,我们的didrecivememwarning 函数
被调用 ,该函数是提醒我们应该释放一些现在用不上的东西,特别是一些较大的
如图片,cache 数据等, 注意如果在这里释放了,代码在用的地方要进行
判断,是否需要重新加载
C. 他会调用didviewunload
这时候我们要注意了,因为他调用了didviewunload,在下次这个view被显示的时候
会再次调用didviewload, 我们在didviewload里分配的娈童就会被分配两次,所以我
们在这里一定要释放didview里分配的变量,不管直接分配的还是间接分配的,
如发消息给其它函数分配的。 那我们在下次分配之前释放一次行不行, 不行,因为这
时候内存吃紧了,你应该多释放内存,不然你的程序可能会被系统kill. 还有虽然对
[xxx release] 对nil 发 release 是没问题的,但逻辑上让人觉得很奇怪,以为在别的
地方分配过。 所以这里应该释放内存,
如果你是一个属性,用:
self.xxx = nil 比较好,他相当于帮你释放了原来的,还把xxx设置为了nil.
如果不是一个属性 :也最好把xxx= nil. 这样比较安全。如果这个view不再被换入
下一步直接调用 dealloc, 这时候你的dealloc里的[xxx release], 如果这里的xxx不为nil你在
didviewunload里release 了,就非常危险
D. 如果这时候view被释放,会调用 dealloc,
这时候我们应该在dealloc里release
前面我们说了outlet的内存没办法管理(看下生命周期)
@property (nonatomic,retain) IBOutlet UILabel *ilblTestMem;
init retain is 2
didviewLoad:
= 2
if(memwarning)
memwaring - retain count -1
= 1
viewdidunload: -1
=0
will load viewDidLoad when next load view
else
dealloc: -1
=1
第一种情况
A. didviewLoad:
retain count 为2
B. dealloc:
-1 retain count 为1
第二种情况:
B. memwarning
系统会把 retain count 减到1
C. viewdidunload
我们应该release 一次 这样 retain count 到0,变量会马上dealloc,更快的释放内存
注意用 self.ilblTestMem = nil 设置为nil, 为 dealloc 做准备
D. dealloc:
-1
NSObject *sMessage = [[NSObjectalloc]init];
//1
[sMessagerelease]; //1
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
[sMessagerelease]; //0
//not crash (retainCount also can call)
//crash (can not call)
[sMessagerelease];
@property (nonatomic, retain) TestObject testObject*; //为testObject成员变量生成属性方法
@end
iPhone/Mac Objective-C内存管理教程和原理剖析
前言
初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃。我在这里总结了自己对objective-C内存管理机制的研究成果和经验,写了这么一个由浅入深的教程。希望对大家有所帮助,也欢迎大家一起探讨。
此文涉及的内存管理是针对于继承于NSObject的Class。
一 基本原理
Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。
1 Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。
ClassA *obj1 = [[ClassA alloc] init];
2 Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。
[obj1 dealloc];
这带来了一个问题。下面代码中obj2是否需要调用dealloc?
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello]; //输出hello
[obj1 dealloc];
[obj2 hello]; //能够执行这一行和下一行吗?
[obj2 dealloc];
不能,因为obj1和obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello]和[obj2 dealloc]。obj2实际上是个无效指针。
如何避免无效指针?请看下一条。
3 Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。
ClassA *obj1 = [[ClassA alloc] init]; //对象生成时,retain count = 1
[obj1 release]; //release使retain count减1,retain count = 0,dealloc自动被调用,对象被销毁
我们回头看看刚刚那个无效指针的问题,把dealloc改成release解决了吗?
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 0,对象被销毁
[obj2 hello];
[obj2 release];
[obj1 release]之后,obj2依然是个无效指针。问题依然没有解决。解决方法见下一条。
4 Objective-C指针赋值时,retain count不会自动增加,需要手动retain。
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 2 – 1 = 1
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 0,对象被销毁
问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。(1-4可以参考附件中的示例程序memman-no-pool.m)
这样的确不会内存泄露,但似乎有点麻烦,有没有简单点的方法?见下一条。
5 Objective-C中引入了autorelease pool(自动释放对象池),在遵守一些规则的情况下,可以自动释放对象。(autorelease pool依然不是.Net/Java那种全自动的垃圾回收机制)
5.1 新生成的对象,只要调用autorelease就行了,无需再调用release!
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release
5.2 对于存在指针赋值的情况,代码与前面类似。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
//对于obj1,无需调用(实际上不能调用)release
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 2-1 = 1
细心的读者肯定能发现这个对象没有被销毁,何时销毁呢?谁去销毁它?(可以参考附件中的示例程序memman-with-pool.m)请看下一条。
6 autorelease pool原理剖析。(其实很简单的,一定要坚持看下去,否则还是不能理解Objective-C的内存管理机制。)
6.1 autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6.2 NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中
6.3 NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。
6.4 默认只有一个autorelease pool,通常类似于下面这个例子。
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
// do something
[pool release];
return (0);
} // main
所有标记为autorelease的对象都只有在这个pool销毁时才被销毁。如果你有大量的对象标记为autorelease,这显然不能很好的利用内存,在iphone这种内存受限的程序中是很容易造成内存不足的。例如:
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
for (j = 0; j < 100000; j++ )
[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
}
[pool release];
return (0);
} // main
(可以参考附件中的示例程序memman-many-objs-one-pool.m,运行时通过监控工具可以发现使用的内存在急剧增加,直到pool销毁时才被释放)你需要考虑下一条。
7 Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyytt和neogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
for (j = 0; j < 100000; j++ )
[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
[loopPool release];
}
[pool release];
return (0);
} // main
二 口诀与范式
1 口诀。
1.1 谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过alloc、new或copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用release或autorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。
1.2 除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。
1.3 谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。不知道为什么苹果公司的文档没有强调这个非常重要的一点,请参考范式2.7和第三章。
2 范式。
范式就是模板,就是依葫芦画瓢。由于不同人有不同的理解和习惯,我总结的范式不一定适合所有人,但我能保证照着这样做不会出问题。
2.1 创建一个对象。
ClassA *obj1 = [[ClassA alloc] init];
2.2 创建一个autorelease的对象。
ClassA *obj1 = [[[ClassA alloc] init] autorelease];
2.3 Release一个对象后,立即把指针清空。(顺便说一句,release一个空指针是合法的,但不会发生任何事情)
[obj1 release];
obj1 = nil;
2.4 指针赋值给另一个指针。
ClassA *obj2 = obj1;
[obj2 retain];
//do something
[obj2 release];
obj2 = nil;
2.5 在一个函数中创建并返回对象,需要把这个对象设置为autorelease
ClassA *Func1()
{
ClassA *obj = [[[ClassA alloc]init]autorelease];
return obj;
}
2.6 在子类的dealloc方法中调用基类的dealloc方法
-(void) dealloc
{
…
[super dealloc];
}
2.7 在一个class中创建和使用property。
2.7.1 声明一个成员变量。
ClassB *objB;
2.7.2 声明property,加上retain参数。
@property (retain) ClassB* objB;
2.7.3 定义property。(property的默认实现请看第三章)
@synthesize objB;
2.7.4 除了dealloc方法以外,始终用.操作符的方式来调用property。
self.objB 或者objA.objB
2.7.5 在dealloc方法中release这个成员变量。
[objB release];
示例代码如下(详细代码请参考附件中的memman-property.m,你需要特别留意对象是在何时被销毁的。):
@interface ClassA : NSObject
{
ClassB* objB;
}
@property (retain) ClassB* objB;
@end
@implementation ClassA
@synthesize objB;
-(void) dealloc
{
[objB release];
[super dealloc];
}
@end
2.7.6 给这个property赋值时,有手动release和autorelease两种方式。
void funcNoAutorelease()
{
ClassB *objB1 = [[ClassB alloc]init];
ClassA *objA = [[ClassA alloc]init];
objA.objB = objB1;
[objB1 release];
[objA release];
}
void funcAutorelease()
{
ClassB *objB1 = [[[ClassB alloc]init] autorelease];
ClassA *objA = [[[ClassA alloc]init] autorelease];
objA.objB = objB1;
}
三 @property (retain)和@synthesize的默认实现
在这里解释一下@property (retain) ClassB* objB;和@synthesize objB;背后到底发生了什么(retain property的默认实现)。property实际上是getter和setter,针对有retain参数的property,背后的实现如下(请参考附件中的memman-getter-setter.m,你会发现,结果和memman-property.m一样):
@interface ClassA : NSObject
{
ClassB *objB;
}
-(ClassB *) getObjB;
-(void) setObjB:(ClassB *) value;
@end
@implementation ClassA
-(ClassB*) getObjB
{
return objB;
}
-(void) setObjB:(ClassB*) value
{
if (objB != value)
{
[objB release];
objB = [value retain];
}
}
在setObjB中,如果新设定的值和原值不同的话,必须要把原值对象release一次,这样才能保证retain count是正确的。
由于我们在class内部retain了一次(虽然是默认实现的),所以我们要在dealloc方法中release这个成员变量。
-(void) dealloc
{
[objB release];
[super dealloc];
}
四 系统自动创建新的autorelease pool
在生成新的Run Loop的时候,系统会自动创建新的autorelease pool(非常感谢网友hhyytt和neogui的提醒)。注意,此处不同于xcode在新建项目时自动生成的代码中加入的autorelease pool,xcode生成的代码可以被删除,但系统自动创建的新的autorelease pool是无法删除的(对于无Garbage Collection的环境来说)。Objective-C没有给出实现代码,官方文档也没有说明,但我们可以通过小程序来证明。
在这个小程序中,我们先生成了一个autorelease pool,然后生成一个autorelease的ClassA的实例,再在一个新的run loop中生成一个autorelease的ClassB的对象(注意,我们并没有手动在新run loop中生成autorelease pool)。精简的示例代码如下,详细代码请见附件中的memman-run-loop-with-pool.m。
int main(int argc, char**argv)
{
NSLog(@"create an autorelasePool\n");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"create an instance of ClassA and autorelease\n");
ClassA *obj1 = [[[ClassA alloc] init] autorelease];
NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:0.0
target:obj1
selector:@selector(createClassB)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
[now release];
[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例
NSLog(@"releasing autorelasePool\n");
[pool release];
NSLog(@"autorelasePool is released\n");
return 0;
}
输出如下:
create an autorelasePool
create an instance of ClassA and autorelease
create an instance of ClassB and autorelease
ClassB destroyed
releasing autorelasePool
ClassA destroyed
autorelasePool is released
注意在我们销毁autorelease pool之前,ClassB的autorelease实例就已经被销毁了。
有人可能会说,这并不能说明新的run loop自动生成了一个新的autorelease pool,说不定还只是用了老的autorelease pool,只不过后来drain了一次而已。我们可以在main函数中不生成autorelease pool。精简的示例代码如下,详细代码请见附件中的memman-run-loop-without-pool.m。
int main(int argc, char**argv)
{
NSLog(@"No autorelasePool created\n");
NSLog(@"create an instance of ClassA\n");
ClassA *obj1 = [[ClassA alloc] init];
NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:0.0
target:obj1
selector:@selector(createClassB)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
[now release];
[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例
NSLog(@"Manually release the instance of ClassA\n");
[obj1 release];
return 0;
}
输出如下:
No autorelasePool created
create an instance of ClassA
create an instance of ClassB and autorelease
ClassB destroyed
Manually release the instance of ClassA
ClassA destroyed
我们可以看出来,我们并没有创建任何autorelease pool,可是ClassB的实例依然被自动销毁了,这说明新的run loop自动创建了一个autorelease pool,这个pool在新的run loop结束的时候会销毁自己(并自动release所包含的对象)。
补充说明
在研究retain count的时候,我不建议用NSString。因为在下面的语句中,
NSString *str1 = @”constant string”;
str1的retain count是个很大的数字。Objective-C对常量字符串做了特殊处理。
当然,如果你这样创建NSString,得到的retain count依然为1
NSString *str2 = [NSString stringWithFormat:@”123”];
涉及的示例程序代码(已去除隐藏,觉得有用的话请顶一下此文):http://files.cnblogs.com/VinceYuan/objective-c-memman.zip
http://blog.csdn.net/weiqubo/article/details/7376189
发表评论
-
iphone编程之UILabel 用法详解
2012-12-18 14:38 1406这段代码动态的创建了一个UILabel,并且把相关常用的属 ... -
IOS用正则验证手机号
2012-12-14 12:01 17209- (BOOL)validateMobile:(NSStr ... -
UITableView技巧之去除UITableViewCell边框
2012-12-14 10:42 3605有时候想在UITableViewCell中加内容,但又不想要c ... -
MAC MySQL Workbench执行批量更新和删除的时候错误解决
2012-10-25 11:21 4002处理MySQL更新表时ErrorCode:1175.You a ... -
Objective-C编码风格指南
2012-10-17 17:00 1228参考资料: • Apple: Coding Guideline ... -
autorelease对象具体什么时候释放?
2012-09-19 15:21 1041在项目中,会有一个默认的Autorelease pool,程序 ... -
Objective-C内存管理教程和原理剖析
2012-09-10 14:44 898此文涉及的内存管理是针对于继承于NSObject的Clas ... -
IOS开发内存释放小结
2012-09-03 17:34 5366内存释放是iphone开发过程中比较重的地方,所以在开辟内存后 ... -
为什么Android没有iOS那么顺滑
2012-08-29 14:38 903虽然很多Android手机的配 ... -
UITextView关闭键盘
2012-08-29 14:13 142121.如果你程序是有导航条的,可以在导航条上面加多一个Done的 ... -
Mac系统下查看鼠标所在点的RGB值--数码测色计
2012-08-29 14:05 1796苹果电脑的Mac OS X系统自带鼠标所在点颜色RGB值查看工 ... -
UIView你知道多少
2012-08-21 09:49 843曾经有人这么说过,在i ... -
Objective-C内存管理总结〜CC专版
2012-08-17 18:24 821iPhone系统中的Objective-C的内存管理机制是比较 ... -
IOS 获取当前系统时间的年、月、日、小时、分、秒
2012-08-14 17:33 6624NSCalendar *calendar = [[NSCa ... -
IOS判断是否为数字
2012-08-13 17:59 4910判断是否为整形: - (BOOL)isPureInt:(N ... -
self.用法
2012-08-09 14:55 849MyClass.h @interface MyClass ... -
#import与@class的区别
2012-08-08 17:13 9401.import会包含这个类的所有信息,包括实体变量和方法,而 ... -
iphone图标去掉光晕效果
2012-08-07 16:53 1161图片背景是透明的 如果想去掉光晕效果,就在info.plist ... -
去掉 App Store 内图标上部高亮效果的办法
2012-08-07 16:52 891苹果默认会在 App Store 里的应用图标上半部自 ... -
继承UIView时使用xib文件布局的方法以及关键问题
2012-08-02 18:06 43911. 首先,新建一个类,继承自UIView。 2. 创建一个空 ...
相关推荐
庄延军所著的《iOS内存管理及优化》是一本专注于iOS平台内存管理技巧与性能优化的书籍。它旨在帮助读者深入理解iOS内存管理机制,掌握内存优化方法,从而提升应用性能、稳定性和用户体验。 内存管理是移动应用开发...
IOS 内存管理 详细讲解iOS内存管理,对于深入研究内存管理的同学必须下载啊!
iOS内存管理1 iOS内存管理是iOS开发中非常重要的一部分,主要是为了避免内存泄露和循环引用。iOS内存管理有两种方式,一种是 Manual Reference Counting(MRC),一种是 Automatic Reference Counting(ARC)。 ...
首先,我们来谈谈iOS内存管理。iOS系统采用自动引用计数(Automatic Reference Counting, ARC)作为主要的内存管理机制。ARC负责跟踪对象的所有者,并在适当的时候释放不再使用的内存。理解如何正确使用ARC至关重要...
本文将深入探讨iOS内存管理中的一个重要概念——深浅拷贝,以及与其相关的`retain`、`copy`和`retainCount`。这些概念对于理解Objective-C中的内存管理机制至关重要。 首先,我们需要了解iOS内存管理的基本原则,即...
标题中的"iOS高级内存管理ARC模式手动模式英文文档含示例工程"表明,这个压缩包提供了深入探讨iOS内存管理的资源,包括了ARC和非ARC(即手动内存管理)的相关内容。这可能包括如何在ARC环境下处理特殊情况,以及何时...
### iOS内存管理核心知识点 #### 一、前言与背景 在探讨iOS内存管理之前,有必要理解为何这一主题如此重要。对于从C++或Java转向Objective-C(简称OC)的开发者来说,OC语言的特性显得既独特又挑战性十足。与Java...
在iOS开发中,内存管理是...总之,理解并熟练掌握这些iOS内存管理的知识点,对于编写高效、稳定的应用至关重要。在面试中,能深入讨论这些问题,不仅体现了开发者的专业技能,也显示了其对程序性能优化的关注和重视。
ios移动开发的内存管理ppt,讲的不错,你值得拥有
放入栈中。此区域的内存由编译器...了解并掌握内存管理是开发iOS应用的关键,合理的内存管理能确保应用的性能和稳定性。无论是手动的MRC还是自动的ARC,都需要开发者对内存管理有深入的理解,防止内存泄漏和意外崩溃。
本文将详细介绍iOS内存管理的知识点,包括内存管理的基本概念、策略、最佳实践和实战技巧,以及Autorelease池的使用等。 ### 1. 内存管理概述 内存管理是指在程序运行时对内存进行分配、使用和释放的过程。在...
IOS的内存管理详解。 Delegate具体详解,使用流程。 Speex的语音压缩技术。 完整详细解读。40页,通俗简单,深入浅出,并有代码示例ppt。
### iOS内存管理的核心概念 #### ARC (Automatic Reference Counting) ARC是苹果在iOS 5中引入的一项技术,用于自动管理对象的引用计数,从而避免了手动管理内存带来的复杂性和错误。通过ARC,编译器能够自动插入...
2. **Android和iOS内存管理**:Android和iOS操作系统都有自己的内存管理机制。Android使用Dalvik或ART虚拟机进行内存分配和回收,而iOS则依赖于Objective-C/Swift的自动引用计数(Automatic Reference Counting, ARC)...
iOS开发有关object-c内存管理!
iOS内存管理的核心在于引用计数(Reference Counting)机制,这是Objective-C中的基础概念。当对象的引用计数为0时,系统会自动回收该对象的内存。本文将深入探讨iOS内存管理的规则及其背后的思考。 1. **生成并...
3.未初始化数据:末初始化的全局变量、静态变量等 1.对字符串,字典,数组进行mutableCopy操作,属于深拷贝,并且会生成一个新的可变对象 2.对可变字符
本文主要讨论的是iOS内存管理中的引用计数机制,这是Objective-C和Swift(早期版本)中的一种自动内存管理策略。 引用计数是一种跟踪和管理对象生命周期的方法。简单来说,一个对象的引用计数表示了有多少个指针...