我知道 Cocoa 中关于内存管理(保留计数、自动释放池等)的基本原则,但是一旦你通过简单的保留/释放,它就会变得更加困惑。我找不到合适的答案,因为大多数教程都涵盖了简单的场景。我想问一下如何编写代码和避免泄漏的最佳实践。
第一个问题 - 迭代和临时分配:
for (id object in objectArray) {
Model *currentItem = object;
/* do something with currentItem */
[currentItem release];
}
Model *model = [unarchiver decodeObjectForKey:@"ARCHIVED_MODEL_OBJECT"];
// it has to be here, because (I was told) unarchiver will return autorelease object
[model retain];
label.text = model.data;
- (IBAction) loadXMLButtonClicked:(id) sender {
objectArray = [self loadData]; // 1 - objectArray is instance var
NSArray *objectArray = [self loadData]; // 2 - objectArray is local var
// loadXMLButtonClicked is called on button click, here the method finishes
// and control goes back to application, autorelease pool is cleaned?
// case 1 - objectArray stays retained in instance variable? (because setter was used)
// case 2 - objectArray is soon to be released, there were no retains?
// (ignore the fact that it's local var, just hypothetically)
}
- (NSArray *) loadData {
NSArray *objectArray = [[NSArray alloc] init];
// populate array here
return [objectArray autorelease];
}
gdb用于打印retainCounts、zombies 等,虽然我可以设法让它运行并且没有泄漏,但我不知道100%为什么并且想听到一个很好的推理,应该如何通过解释来做到这一点。那将不胜感激。- (NSArray *) loadData {
(...)
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
ModelXMLParser *parserDelegate = [[ModelXMLParser alloc] init];
[parser setDelegate:parserDelegate];
[parser parse];
objectArray = [[parserDelegate objectArray] copy];
// is this ok? *i* don't need the parser object so I think I should get rid of it
// and copy the data. How this copy works, is it shallow (only new reference to array)
// or deep copy (objects allocated again as well)?
// how to do deep copy of NSArray?
[parserDelegate release];
[parser release];
}
@implementation ModelXMLParser
@synthesize objectArray; // array of objects
@synthesize currentObject; // temporary object
@synthesize currentChars; // temporary chars
@synthesize parseChars; // parse chars only when there's need, leave those /t/n etc
- parser didStartElement (...) {
if ([elementName isEqualToString:@"objects"]) {
objectArray = [[NSMutableArray alloc] init];
}
else if ([elementName isEqualToString:@"object"]) {
currentObject = [[Model alloc] init];
}
else if ([elementName isEqualToString:@"name"]) {
// do I have to init currentObject.name (NSString) here? I guess not
[self setParseChars:YES]; // just set the flag to make parse control easier
}
else if ([elementName isEqualToString:@"number"]) {
// int isn't object anyway, no init
[self setParseChars:YES]; // just set the flag to make parse control easier
}
}
- parser foundCharacters (...) {
if (parseChars) {
currentChars = [[NSString alloc] initWithString:string];
// why is currentChars retainCount = 2 here?
// is it like currentChars = [NSString new] and then currentChars = string? (so retain once more)
// is it good way to control parser? (please ignore the NSMutableString and appending example, try this one)
// should I just do currentChars = string here?
[currentChars autorelease]; // this is currently my solution, because there's no leak, but I feel it's incorrect
}
}
- parser didEndElement (...) {
if ([elementName isEqualToString:@"object"]) {
[objectArray addObject:[currentObject copy]]; // should I copy here or just addObject, it retains anyway?
[currentObject release]; // I've initialized currentObject before, now I don't need it, so I guess retainCount goes to 0 here?
}
else if ([elementName isEqualToString:@"name"]) {
currentObject.name = currentChars; // is this correct, or shoud I do [currentChars copy] as well?
[self setParseChars:NO];
[currentChars release]; // as before, initialized, now releasing, but is this really correct?
}
else if ([elementName isEqualToString:@"number"]) {
currentObject.number = [currentChars intValue]; // is this correct, or shoud I do [currentChars copy] as well?
[self setParseChars:NO];
[currentChars release]; // as before, initialized, now releasing, but is this really correct?
}
}
- (void) dealloc {
// I shouldn't release currentChars or currentObject, those (I suppose) should be freed after parsing done,
// as a result of earlier releases?
[objectArray release];
[super dealloc];
}
@implementation Model
@synthesize name; // this is NSString
@synthesize number; // this is int
- (id) copyWithZone:(NSZone *) zone {
Model *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name copy];
copy.number = self.number;
return copy;
}
- (void) dealloc {
[name release];
// I don't have to release int, right? it's not an object
[super dealloc];
}
最佳答案
第一个问题 - 迭代和临时分配
您不应该在此处释放对象,因为您不是所有者。如果您是对象的所有者,您应该只释放它。见 Memory Management Guide for Cocoa .如果您调用名称以 init 开头的方法,您只是对象的所有者。 , new ,或包含 copy以其名义。
由于 for 循环不使用具有任何这些名称的方法,因此您不是所有者,因此您不得释放对象。这将导致在对象完成之前释放对象,这几乎肯定会导致内存损坏和崩溃。
第二个问题 - 即时保留:
你不需要立即调用保留,你只需要在自动释放池下一次清空之前调用它。这可能是在您的方法返回主事件循环后不久。由于您不知道这会在何时发生,您必须确保如果您希望能够在函数(在本例中为 loadXMLButtonClicked:)返回后访问该对象,那么您必须 retain在你回来之前。
自 decodeObjectForKey不以 init 开头或 new或包含 copy以它的名义,您不会成为所有者。调用 retain使您成为所有者。
第三个问题 - 自动释放和传递变量:
首先,用同名的局部变量隐藏类成员是不好的做法。其次,除非loadData被用作多用途实用程序函数(我猜它不是,因为它不带任何参数),它应该只将结果直接分配给成员变量 objectArray .返回结果然后将调用函数分配给成员变量是毫无意义且容易出错的。
第三,您没有使用 objectArray属性 setter ——您只是直接分配给成员变量。如果要使用setter,必须明确说self.objectArray = ... (前面有 self.)。因此,objectArray永远不会保留,所以它会在下一次自动释放池清除时被释放,这不是你想要的。你必须在某个时候保留它,或者相反,只是不要在 loadData 结束时自动释放它。 ,并为其分配类成员变量 objectArray .
如果属性是用 retain 声明的属性,然后使用setter 会自动调用retain当你用它赋值时(它也会 release 旧值)。如果相反,该属性是用 copy 声明的属性,那么每次分配给它时都会复制该值,并且您将成为新对象的所有者。
第 4 个问题 - 解析器 xml 最佳实践:
您正在制作对象数组的浅拷贝。如果要进行深拷贝,可以使用 initWithArray:copyItems: 信息。
do i have to init currentObject.name (NSString) here? i guess not?
currentObject.name在该代码附近的任何地方都提到过。why is currentChars retainCount = 2 here?
retain在某处多写了一些时间,但几乎可以肯定是 autoreleased还有额外的时间。如果您遵循 Cocoa 内存管理指南中的所有规则,您就不会有任何问题。你永远不应该依赖保留计数,因为你不知道一个对象被自动释放了多少次。它们是调试辅助工具,而不是应该用于程序控制流的东西。this is currently my solution, because there's no leak, but i feel it's incorrect?
currentChars下次您返回事件循环时,就可以了。如果你需要使用它,你不应该在这里释放或自动释放它,然后在你确定你完成它时释放它。should I copy here or just addObject, it retains anyway?
addObject :当您将项目添加到 NSArray 中时, NSSet , 或 NSDictionary ,它们会自动 retain由数据结构编辑。当您删除它们时,它们是 release d.关于iphone - Objective-C 内存管理、xml 解析器和其他重要示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/546588/
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el