Как управлять памятью с помощью классов в Objective-C? - PullRequest
1 голос
/ 29 декабря 2010

Это мой первый раз, когда я создаю приложение для 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 имеет метод getName , который возвращает NSString *.Кажется, проблема в том, где устанавливается мелок и вызывается getName .Я попытался добавить autorelease , release и другие подобные вещи, но это просто приводит к сбою всего приложения перед его запуском.

Также, если я это сделаю:

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) (вт. 14 июля 02:11:58 UTC 2009) Copyright 2004 FreeSoftware Foundation, Inc. GDB - это бесплатное программное обеспечение, на которое распространяется Стандартная общественная лицензия GNU, и вы можете изменять его и / или распространять его копии при определенных условиях.Введите «показать копирование», чтобы увидеть условия.На GDB нет абсолютно никаких гарантий.Тип "показать гарантию" для деталей.Этот 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.платформа / разработчик / 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] [0x12358f] [3x12358f] [3x12358f] 3/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (dyld_info_read_raw_data + 0x50) [0x1659af] [4] /Developer/Platforms/iPhoneSimulator.platrglibgdb-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/form_Platforms/ usr / libexec / gdb / gdb-i386-apple-darwin (macosx_solib_add + 0x36b) [0x169fcc] [7] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-idwin (macosx_child_attach + 0x478) [0x17dd11] [8] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (attach_command + 0x5d) [0x64ec /] Platform [0x64ec5] 9iPhoneSimulator.platform / Developer / usr / libexec / gdb / gdb-i386-apple-darwin (mi_cmd_target_attach + 0x4c) [0x15dbd] [10] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/ib6g-apple-darwin (captured_mi_execute_command + 0x16d) [0x17427] [11] /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/libexec/gdb/gdb-i386-apple-darwin (catch_exception + 0x7] [0xa] [0xa] [0a] [0]/ Разработчик / Платформы /iPhoneSimulator.platform / Разработчик / usr / libexec / gdb / gdb-i386-apple-darwin (mi_execute_command + 0xa9) [0x16f63]/SourceCache/gdb/gdb-967/src/gdb/utils.c:1144: внутренняя ошибка: виртуальная память исчерпана: невозможно выделить 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 ]

2 голосов
/ 29 декабря 2010

Всего в стороне - не прямая причина вашего сбоя, но указывает на характер использования, который чужд стандартным шаблонам 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 голосов
/ 29 декабря 2010

Во-первых, если происходит сбой, есть обратный след.Отправьте это.Увидел ваше редактирование;установить точку останова на malloc_error_break и опубликовать обратную трассировку.Обычно такой сбой malloc () означает, что вы либо исчерпали 32-битное адресное пространство , либо , и вы вызвали передачу богонского значения в malloc.

Во-вторых, если у вас естьинструменты управления памятью, инструменты могут помочь вам отследить ее.Запустите ваше приложение с помощью инструмента Allocations и попробуйте прокрутить.Должно быть совершенно очевидно, что выделяется либо так часто, чтобы вызвать такой сбой.

Вы сказали:

Кажется, проблема в той части, где установлен карандаш, ивызывается getName.

Если проблема связана с getName, пожалуйста, отправьте код для getName.Как бы то ни было, в этом коде нет ничего, что казалось бы явно чрезмерно занимало память.

Метод getName выглядит фантастически простым.:)

В консоли отладчика, если вы введете b malloc_error_break, будет установлена ​​точка останова.Или вы можете установить точку останова как «символическую точку останова» в окне отладчика Xcode.

Инструменты - это другой инструмент;вы можете запустить «Run -> Run With Performance Tool -> Allocations», чтобы запустить ваше приложение под инструментами.

Однако, учитывая совершенно поддельное значение, передаваемое malloc (), я подозреваю, что что-то еще пошло не так.

Эта обратная трассировка на самом деле весьма полезна.Это указывает на то, что ошибка malloc () происходит, когда ячейка пытается выделить что-то, связанное с настройкой текста.

И вы также сказали, что если вы добавите cell.textLabel.text = @"test";, то ошибкаперестает происходить.

Моя лучшая догадка;Вы либо перевыпускаете, либо портите строку, которая должна быть визуализирована как текст cell.textLabel.

В частности, я бы поспорил, что crayon переиздан или поврежден в некоторых случаях.обстоятельства.Попробуйте добавить NSLog(@"crayon %@", crayon); в ваш код и посмотрите, будет ли он тоже barfs.И, как кто-то предложил, попробуйте запустить с включенным обнаружением зомби (Выполнить -> Запустить с Performance Tools -> Обнаружение зомби; или как там это называется).

1 голос
/ 29 декабря 2010

В вашем initToCall: методе замените:

name = toSetName;

с:

name = [toSetName retain];

Даже если вы установите свойство как свойство «сохранить», ваше первоначальное значение не сохраняется, поскольку вы назначаете его напрямую. С точки зрения Какао, вы никогда не «становитесь владельцем» этого. Поскольку строковое значение, в которое вы были переданы, не принадлежит вам, оно в конечном итоге исчезает, и именно поэтому вы видите сбой. Если вы присваиваете ivars непосредственно в методах init, вы должны помнить, чтобы сохранить или скопировать их самостоятельно. Другой вариант - использовать аксессоры:

self.name = toSetName;

В этом случае вы автоматически вызовете ваш синтезированный установщик, который сохранит это свойство для вас (поскольку вы указали его как свойство «сохранить»).

РЕДАКТИРОВАТЬ: Кроме того, хотя это не имеет прямого отношения к вашей проблеме здесь, это хорошая форма всегда вызывать назначенный инициализатор вашего суперкласса из вашего собственного назначенного инициализатора. Итак, общая схема будет такой:

- (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;
}
0 голосов
/ 29 декабря 2010

Ваш метод getName неверен.Необходимо вернуть объект с счетом сохранения +0 (сохраняется и автоматически высвобождается в контексте автоматического выпуска вызывающей стороны).

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

Как уже говорили другие, используя имя @synthesize для создания - (NSString *)метод name и вызов его вместо этого - лучшая идея.

Ошибка, вызывающая обнаруженную вами ошибку, почти наверняка содержится в коде, в котором установлено имя;Я подозреваю, что вы не сохранили значение правильно.Опять же, если вы используете @synthesize name и вызываете сгенерированный метод - (void) setName: (NSString *), чтобы установить имя, он будет работать правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...