如何使用Objective-C中的类来管理内存?

时间:2010-12-29 09:07:46

标签: iphone objective-c memory-management

这是我第一次创建iPhone应用程序而且我在内存管理方面遇到了困难,因为我以前从未处理过这个问题。

我有一个UITableViewController,它一切正常,直到我尝试在模拟器中向下滚动。它崩溃说它无法分配那么多内存。我把它缩小到发生崩溃的地方:

    - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Dequeue or create a cell
    UITableViewCellStyle style =  UITableViewCellStyleDefault;
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"BaseCell"];
    if (!cell) cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:@"BaseCell"] autorelease];

    NSString* crayon;

    // Retrieve the crayon and its color
    if (aTableView == self.tableView)
    {
        crayon = [[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] getName];
    }
    else
    {
        crayon = [FILTEREDKEYS objectAtIndex:indexPath.row];
    }

    cell.textLabel.text = crayon;

    if (![crayon hasPrefix:@"White"])
        cell.textLabel.textColor = [self.crayonColors objectForKey:crayon];
    else
        cell.textLabel.textColor = [UIColor blackColor];
    return cell;
}

以下是 getName 方法:

- (NSString*)getName
{
    return name;
}

名称定义为:

@property (nonatomic, retain) NSString *name;

现在sectionArray是一个NSMutableArray,其中包含我在其中创建 Term 的类的实例。

Term 有一个返回NSString *的方法 getName 。这个问题似乎是蜡笔设置的部分,并且正在调用 getName 。我尝试添加 autorelease 发布以及其他类似的内容,但这只会导致整个应用在启动之前崩溃。

如果我这样做:

cell.textLabel.text = @"test"; //crayon;

/*if (![crayon hasPrefix:@"White"])
    cell.textLabel.textColor = [self.crayonColors objectForKey:crayon];
else
    cell.textLabel.textColor = [UIColor blackColor];*/

然后我没有得到任何错误,所有这些都滚动得很好。

提前感谢您的帮助!

编辑:

以下是我尝试运行应用时的完整日志以及崩溃时出现的错误:

  

[会议开始于2010-12-29 04:23:38 -0500。]

     

[会议开始于2010-12-29 04:23:44 -0500。]   GNU gdb 6.3.50-20050815(Apple版gdb-967)(2009年7月14日星期二02:11:58)   版权所有2004 Free Software Foundation,Inc。   GDB是免费软件,由GNU通用公共许可证涵盖,您就是   欢迎在某些条件下更改和/或分发它的副本。   输入“show copying”查看条件。   GDB完全没有保修。输入“show warranty”了解详情。   此GDB配置为“i386-apple-darwin”.sharedlibrary apply-load-rules all   附加到过程1429。   gdb-i386-apple-darwin(1430,0x778720)malloc: * mmap(size = 1420296192)失败(错误代码= 12)    错误:无法分配区域   * *在malloc_error_break中设置断点以进行调试   内部错误点的gdb堆栈爬网:   [0] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(align_down + 0x0)[0x1222d8]   [1] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(xstrvprintf + 0x0)[0x12336c]   [2] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(xmalloc + 0x28)[0x12358f]   [3] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(dyld_info_read_raw_data + 0x50)[0x1659af]   [4] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(dyld_info_read + 0x1bc)[0x168a58]   [5] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(macosx_dyld_update + 0xbf)[0x168c9c]   [6] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(macosx_solib_add + 0x36b)[0x169fcc]   [7] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(macosx_child_attach + 0x478)[0x17dd11]   [8] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(attach_command + 0x5d)[0x64ec5]   [9] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(mi_cmd_target_attach + 0x4c)[0x15dbd]   [10] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(captured_mi_execute_command + 0x16d)[0x17427]   [11] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(catch_exception + 0x41)[0x7a99a]   [12] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin(mi_execute_command + 0xa9)[0x16f63]   /SourceCache/gdb/gdb-967/src/gdb/utils.c:1144:internal-error:虚拟内存耗尽:无法分配1420296192个字节。   检测到GDB内部的问题,   进一步的调试可能证明是不可靠的。

     

调试器已退出状态1.调试器已退出状态1.


这是我为malloc_error_break设置断点时得到的回溯:

#0  0x0097a68c in objc_msgSend ()
#1  0x01785bef in -[UILabel setText:] ()
#2  0x000030e0 in -[TableViewController tableView:cellForRowAtIndexPath:] (self=0x421d760, _cmd=0x29cfad8, aTableView=0x4819600, indexPath=0x42190f0) at /Volumes/Main2/Enayet/TableViewController.m:99
#3  0x016cee0c in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] ()
#4  0x016c6a43 in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:] ()
#5  0x016d954f in -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow] ()
#6  0x016d08ff in -[UITableView layoutSubviews] ()
#7  0x03e672b0 in -[CALayer layoutSublayers] ()
#8  0x03e6706f in CALayerLayoutIfNeeded ()
#9  0x03e668c6 in CA::Context::commit_transaction ()
#10 0x03e6653a in CA::Transaction::commit ()
#11 0x03e6e838 in CA::Transaction::observer_callback ()
#12 0x00b00252 in __CFRunLoopDoObservers ()
#13 0x00aff65f in CFRunLoopRunSpecific ()
#14 0x00afec48 in CFRunLoopRunInMode ()
#15 0x00156615 in GSEventRunModal ()
#16 0x001566da in GSEventRun ()
#17 0x01689faf in UIApplicationMain ()
#18 0x00002398 in main (argc=1, argv=0xbfffefb0) at /Volumes/Main2/Enayet/main.m:14

--------


以下是UITableViewController类的完整源代码:

//
//  TableViewController.m
//  Enayet
//
//  Created by Filip on 12/27/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "TableViewController.h"
#import "EnayetAppDelegate.h"

@implementation TableViewController

@synthesize crayonColors;
@synthesize filteredArray;
@synthesize sectionArray;
@synthesize searchBar;
@synthesize searchDC;

- (TableViewController*) initToCall:(NSMutableDictionary*) toUseArray
{
    self = [super initWithStyle:UITableViewStylePlain];
    crayonColors = toUseArray;
    return self;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView 
{ 
    if (aTableView == self.tableView) return 26;
    return 1; 
}

// Via Jack Lucky
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
    [self.searchBar setText:@""]; 
}

- (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section
{
    if (aTableView == self.tableView) 
    {
        if ([[self.sectionArray objectAtIndex:section] count] == 0)
            return nil;
        //return [NSString stringWithFormat:@"%@", [[ALPHA substringFromIndex:section] substringToIndex:1]];
        return [[ALPHA substringFromIndex:section] substringToIndex:1];
    }
    else return nil;
}

- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section 
{
    // Normal table
    if (aTableView == self.tableView) return [[self.sectionArray objectAtIndex:section] count];

    // Search table
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", self.searchBar.text];
    self.filteredArray = [self.crayonColors.allKeys filteredArrayUsingPredicate:predicate];
    return self.filteredArray.count;
}

// Convert a 6-character hex color to a UIColor object
- (UIColor *) getColor: (NSString *) hexColor
{
    unsigned int red, green, blue;
    NSRange range;
    range.length = 2;

    range.location = 0; 
    [[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&red];
    range.location = 2; 
    [[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&green];
    range.location = 4; 
    [[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&blue];   

    return [UIColor colorWithRed:(float)(red/255.0f) green:(float)(green/255.0f) blue:(float)(blue/255.0f) alpha:1.0f];
}

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Dequeue or create a cell
    UITableViewCellStyle style =  UITableViewCellStyleDefault;
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"BaseCell"];
    if (!cell) cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:@"BaseCell"] autorelease];

    NSString* crayon;

    // Retrieve the crayon and its color
    if (aTableView == self.tableView)
    {
        Term *term = (Term *) [[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
        crayon = [term name];
    }
    else
    {
        crayon = [FILTEREDKEYS objectAtIndex:indexPath.row];
    }

    cell.textLabel.text = crayon;

    if (![crayon hasPrefix:@"White"])
        cell.textLabel.textColor = [self.crayonColors objectForKey:crayon];
    else
        cell.textLabel.textColor = [UIColor blackColor];
    return cell;
}

// Grouped Tables do not support section indices (even though they kind of do)

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)aTableView 
{
    if (aTableView == self.tableView)  // regular table
    {
        NSMutableArray *indices = [NSMutableArray arrayWithObject:UITableViewIndexSearch];
        for (int i = 0; i < 26; i++) 
            if ([[self.sectionArray objectAtIndex:i] count])
                [indices addObject:[[ALPHA substringFromIndex:i] substringToIndex:1]];
        // [indices addObject:@"\ue057"]; // <-- using emoji
        return indices;
    }
    else return nil; // search table
}


- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    if (title == UITableViewIndexSearch) return 0;
    return [ALPHA rangeOfString:title].location;
 }

- (void)goToTwo:(Term*) toGive
{
    EnayetAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    [appDelegate setTerm:toGive];
    [appDelegate displayView:2];
}

// Respond to user selections by updating tint colors
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if (aTableView == self.tableView)
    {
        [self goToTwo:[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]];
    }
}

- (void) viewDidLoad
{
    // Prepare the crayon color dictionary
    NSString *pathname = [[NSBundle mainBundle]  pathForResource:@"crayons" ofType:@"txt" inDirectory:@"/"];
    NSArray *rawCrayons = [[NSString stringWithContentsOfFile:pathname encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByString:@"\n"];
    self.crayonColors = [NSMutableDictionary dictionary];

    self.sectionArray = [NSMutableArray array];
    for (int i = 0; i < 26; i++) [self.sectionArray addObject:[NSMutableArray array]];
    for (NSString *string in rawCrayons) 
    {
        [self.crayonColors setObject:CRAYON_COLOR(string) forKey:CRAYON_NAME(string)];
        NSUInteger firstLetter = [ALPHA rangeOfString:[string substringToIndex:1]].location;
        if (firstLetter != NSNotFound) [[self.sectionArray objectAtIndex:firstLetter] addObject:[[Term alloc] initToCall:CRAYON_NAME(string)]];
    }
    //NSLog(@"%@", sectionArray);

    // Create a search bar
    self.searchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)] autorelease];
    self.searchBar.tintColor = COOKBOOK_PURPLE_COLOR;
    self.searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
    self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.searchBar.keyboardType = UIKeyboardTypeAlphabet;
    self.tableView.tableHeaderView = self.searchBar;

    // Create the search display controller
    self.searchDC = [[[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self] autorelease];
    self.searchDC.searchResultsDataSource = self;
    self.searchDC.searchResultsDelegate = self;
}
@end

------


这是我的Term.m文件:

//
//  Term.m
//  Enayet
//
//  Created by Filip on 12/27/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "Term.h"


@implementation Term

@synthesize name;

- (Term*)initToCall:(NSString*) toSetName
{
    name = toSetName;
    return self;
}

- (NSString*)getName
{
    return name;
}

-(void)dealloc
{
    [name release];
    [super dealloc];
}

@end

4 个答案:

答案 0 :(得分:2)

首先,如果发生崩溃,则会有回溯。发表它。看到你的编辑;在malloc_error_break上设置断点并发布回溯。通常,这样的malloc()失败表示您已经耗尽了32位地址空间,导致将bogon值传递给malloc。

其次,如果您遇到内存管理问题,Instruments可以帮助您跟踪它。使用Allocations工具运行您的应用程序并尝试滚动。显而易见的是,为了引起这样的崩溃而分配的是什么。

你说:

  

问题似乎是问题的一部分   蜡笔正在设置和getName   正在被召唤。

如果问题与getName有关,请发布getName的代码。实际上,代码中没有任何内容显然过于耗费内存。

getName方法看起来非常简单。 :)


在调试器控制台中,如果键入b malloc_error_break,它将设置断点。或者您可以在Xcode的调试器窗口中将断点设置为“符号断点”。

仪器是一种不同的工具;您可以“运行 - &gt;使用Performance Tool运行 - &gt;分配”以在乐器下运行您的应用。

但是,鉴于将完全虚假的值传递给malloc(),我怀疑还有其他错误。


该回溯实际上非常有用。它表示当Cell尝试分配与设置文本相关的内容时发生malloc()错误。

您还说如果添加cell.textLabel.text = @"test";,则错误就会停止。

我最好的猜测;你是过度释放或破坏应该作为cell.textLabel文本呈现的字符串。

特别是,我敢打赌crayon在某些情况下过度释放或腐败。尝试在代码中添加NSLog(@"crayon %@", crayon);,看看它是否也是barfs。并且,正如有人建议的那样,尝试启用僵尸检测(运行 - &gt;使用性能工具运行 - &gt;僵尸检测;或其他任何调用)。

答案 1 :(得分:2)

总而言之 - 不是导致崩溃的直接原因,而是指示与标准iOS模式不同的使用模式。

你说你有:

- (NSString*)getName
{
    return name;
}

@property (nonatomic, retain) NSString *name;

这是错的;如果已为getName定义了属性,则无需定义name方法。只需使用name方法,可以通过直接调用或通过点语法。

就是这样:

crayon = [[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] getName];

应该是:

crayon = [[[self.sectionArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] name];

答案 2 :(得分:1)

initToCall:方法中,替换:

name = toSetName;

使用:

name = [toSetName retain];

即使您将属性设置为“保留”属性,也不会保留原始值,因为您直接指定它。在可可术语中,你永远不会“拥有”它。由于您传入的字符串值不归您所有,因此它最终会消失,这就是您看到崩溃的原因。如果直接在init方法中分配ivars,则必须记住自己保留或复制它们。您的另一个选择是使用访问者:

self.name = toSetName;

在这种情况下,您将自动调用合成的setter,它将为您保留属性(因为您将其指定为“retain”属性)。

编辑:此外,虽然这里与您的问题没有直接关系,但总是从您自己的指定初始化程序调用您的超类的指定初始值设定项是一种很好的形式。所以,一般模式是:

- (id)initToCall:(NSString*)toSetName
{
  if( (self = [super init]) ) { // call NSObject's designated initializer
    // do our initialization
    name = [toSetName retain]; // take ownership of this string
  }
  return self;
}

答案 3 :(得分:0)

你的getName方法错了。它需要返回一个保留计数为+0的对象(在调用者的自动释放上下文中保留和自动释放)。

-(NSString *)getName
{
  return [[name retain] autorelease];
}

正如其他人所说,使用@synthesize名称来创建 - (NSString *)名称方法并调用它是一个更好的主意。

触发您观察到的失败的错误几乎肯定是在设置名称的代码中;我怀疑你没有正确保留这个值。再次,如果您使用@synthesize名称并调用生成的 - (void)setName:(NSString *)方法来设置名称,它将正常工作。