2012-01-11 75 views
0

好的,所以我开发iPhone应用程序的一半,我在内存管理方面一直磕磕绊绊。有人可以解释内存管理

我已经尝试了很多次,以非常有限的成功理解这一点。我认为自己是高于平均水平的情报,但这种东西只是躲开我,经过多次搜索和苹果文档的阅读

比方说,我有我创建一个选择器 - 这样的代码放在

UIPickerView *patientPicker = [[[UIPickerView alloc] init]retain]; 
//more code here 
[self.view addSubView:patientPicker]; 

那么,我会用我的选择器做几件不同的事情。

只有按下分段控制按钮时才会出现选取器。分段控制规定使用哪个数据阵列来填充选择器。

但是,当我更改分段控制时,我发现它在旧选取器的顶部显示了一个新选取器,而不是更改当前选取器中的数据。

即分段控制是患者年龄或体重。如果选择年龄,则会出现年龄选择器,如果选择了重量,则会出现相同的重量选择器。但是,如果其中一个拾取器已经存在,那么单击备用片段不会更改数据,它只是将另一个拾取器添加到视图中。

我的问题来了,当我尝试隐藏选取器,因为旧选取器仍然在下面,我无法隐藏旧选取器。

所以当我点击一个按钮来移除选取器时,旧的选取器仍然存在于下方。

我已经试过

[patientPicker removeFromSuperView]; 

,但是当我试图重建我拾起我所知,病人选择器已释放???

这同样适用于

[patientPicker release]; 

我知道,有人能告诉我简单的回答,但我真正想要的是让我不内存管理的一个非常简单/简单化的解释不得不再问一次。

假装我7岁!

感谢

鲍勃

+1

您需要做以下两件事之一:1)购买一本关于Objective-C/iOS编程的好书,并研究关于内存管理的章节。 2)坚持使用iOS 5/Xcode 4中的ARC(自动引用计数)环境。 – 2012-01-11 01:57:51

+1

(在你做任何一个你需要对OO有很好的基本理解之前,很多人不能理解你可以拥有同一个类的多个不同对象,并且将值存储在一个不会让这个值神奇地出现在另一个中。) – 2012-01-11 02:00:33

+0

@热舔:ARC支持可以从iOS 4.3或更高版本获得。 – 2012-01-11 10:34:39

回答

1
UIPickerView *patientPicker = [[[UIPickerView alloc] init] retain]; 

在这里,您不/不应该retaininit调用已经意味着调用者负责创建对象(换句话说,init具有暗示的retain)。每initretain需要release

0

我觉得每次分段控件改变选择时你都在做alloc/init。根据选择

+0

聪明 - 我一定会尝试!我认为这将为我解决这一切 – 2012-01-14 12:08:56

0

UIPickerView *patientPicker = [[UIPickerView alloc] init; 
//more code here 
[self.view addSubView:patientPicker]; 
patientPicker.hidden = YES; 
[patientPicker release]; 

当选择上段控制方面,拣货机的隐藏属性设置为NO,并设置datasource:你可以做的是,在vieDidLoad,做到这一点这听起来像是一个工具的工作......是的!点击构建和分析,并删除每个问题=)但了解为什么静态分析器标记你的程序,并明白它有很多可以捕获,但很多,它不能证明是引用计数不平衡,并会不标记这些。然后运行泄漏工具并修复所有问题等,如果运行到释放的实例,运行僵尸工具并修复所有问题。不管怎么说,还有更多它!这里有一些指向你的代码。

UIPickerView *patientPicker 
     = [[[UIPickerView alloc] init]retain]; // << do not retain here. alloc 
              // returns an object you must release 

[self.view addSubView:patientPicker]; // << self.view will retain its subviews. 
             // ok, that makes sense that the view 
             // would want to hold onto a reference to 
             // ensure the view is not destroyed 
             // while it's still a subview. 

[patientPicker removeFromSuperView]; << the superview will release its subview 

[patientPicker release]; << your life will be easier if you use the accessors 

当你在处理引用计数时,你需要保存一个引用来使用一个对象。所以,让我们把autorelease池排除在等式之外。只有在需要时才使用autorelease将帮助您学习,并在callsite上提出一些本地问题 - 避免在学习时尽可能地调用autorelease。

NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference 
[a length]; // ok 
[a retain]; // << I hold 2 references 
[a release]; // << I hold 1 reference 
[a release]; // << I hold 0 references 
[a length]; // expect bad things 

现在让我们来说明自动释放池:内存管理

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference 
[a length]; // ok 
[a retain]; // << I hold 2 references 
[a release]; // << I hold 1 reference 
[a autorelease]; // << add a to pool. when pool is destroyed a release message will be sent to a 
[a length]; // ok. a is still alive. its release message is deferred until the pool is destroyed 
[pool release]; // pool's reference count has reached zero. pool will call [a release]. pool will be destroyed. 
[a length]; // expect bad things 
+0

有趣的贾斯汀 - 有没有办法NSlog的引用数量?感谢您提供一个内容丰富的帖子 – 2012-01-14 11:57:23

+0

也可以,我可以指点我可以读更多的地方吗? – 2012-01-14 12:08:09

+0

@Bob有。这就是所谓的乐器。第一站:僵尸仪器 - 移除任何僵尸下一页:泄漏 - 删除所有可能的泄漏。要获得引用计数操作:转到分配工具(泄漏和僵尸为您添加) - 单击“我”。点击“记录参考计数”。记录一些东西停止。将分配视为“对象列表”。找到并选择分配。点击地址附近的箭头。 cmd + E显示扩展信息。仪器已记录了所有参考计数操作的调用堆栈。 – justin 2012-01-14 19:05:54

0

两个规则:

  • 如果newalloc initretain,或copy(NARC)的对象,则有释放它。
  • 当方法的名称以任何这些词开头时,表示它正在为调用者创建,调用者在完成对象时负责对象release。否则返回的方法不是由调用者拥有的,他必须表明他想保持对该对象调用retain

请注意,第一个规则导致第二个。一个方法创建一个对象(因此它负责释放它),但是如果方法的结果(返回的对象)在方法的执行中存活(所以它可以交给调用者),所有的方法都可以自动释放目的。 autorelease将该对象添加到autorelease池中,以便将来在某个时刻释放该对象。

例子:

[[MyObject new]; // 1) 
[NSString initWithFormat:@"%d",1]; // 2) 
[NSString string]; // 3) 

1)名称中包含新的,所以它需要释放。
2)名称包含init,所以它需要释放。
3)名称不包含任何NARC单词,因此您知道它会返回一个自动释放对象。这意味着如果您打算保留它,则需要保留它,如果这样做,则需要稍后再发布。否则,只要使用它,忘掉它。

小贴士:

  • 尽量保留/释放对称,这样你就不会失去跟踪的内容发布和地点。例如:如果您保留在init上,则释放dealloc。相同的viewDidLoad/viewDidUnload。
  • 如果您可以选择,请不要在内存不足时滥用autorelease,以便尽快恢复内存。滥用autorelease也是一种你不了解内存管理的迹象。

你举的例子(同样的事情贾斯汀告诉你):

UIPickerView *patientPicker = [[UIPickerView alloc] init]; // 1) 
[self.view addSubView:patientPicker]; // 2) 
[patientPicker release]; // 3) 

1)我们呼吁的alloc初始化,所以你知道这个对象就需要释放,一旦你用它做。
2)addSubView调用保留在内部。为什么?当你收到一个对象并且你打算保留这个对象时,你表达了保留的意图,这也给你释放它的责任。当一个UIView被释放时,它的实现也会释放它的子视图来平衡addSubView之前完成的保留。
3)我们不会再使用它,所以我们称之为释放。现在self.view是对象的所有者。

作为练习,尝试为变量实现自己的setter和getter并运行Build and Analyze来查看Xcode对它的看法。

+0

好吧,我得到你在说什么,特别是NARC位。但是,如果不是UIPickerView * pickerView = [[UIPickerView alloc] init];等我放在.h文件@property(非原子,保留)UIPickerView * patientPicker,然后写pickerView = [[UIPickerView alloc] init];这将改变什么(如果它确实如此?) – 2012-01-14 12:00:57

+0

'UIPickerView * pickerView = [[UIPickerView alloc] init]; self.pickerView = pickerView; [pickerView发布]; - (void)dealloc {[pickerView release]; [super dealloc]; }'。这是1)alloc + init,2)保留从@property,3)对象的释放(现在它属于属性,你不需要它)。并且当类被释放时4)dealloc释放ivars并且你完成了。保留/释放对是1,3和2,4。 – Jano 2012-01-14 14:52:23

+0

如果你也做'[self.view addSubView:picker]',那会导致另一个保留,然后当视图被释放时,它将释放它的所有子视图,以平衡保留在addSubView中。我不知道为什么你在设置UIPickerViewDelegate之后还想把选取器当成ivar,但是无论如何,对于内存管理来说都没问题。 – Jano 2012-01-14 15:04:41

0

专注于所有权,如果你拥有所有权;你必须要release对象。如果你没有所有权,就不敢去release吧。


id myObject;
myObject = [abc retain] =>你是主人

myObject = [[abc alloc] init] =>你是主人

myObject = [abc copy] =>你是主人


myObject = [[[abc alloc] init] autorelease] =>你不是店主
(无处不在,你把autorelease你失去所有权)

myObject = [abc xxxWithYYY] =>你是不是所有者
(按照惯例,一个方法返回的对象总是给一个autorelease对象)


会有一些更多类似的可以识别OWNERSHIP的约定;我只记下了我现在可以记得的东西。