2009-09-14 5 views
2

Я испытываю жесткий вопрос здесь, был бы признателен за любую, я имею в виду какой-либо помощи =)NSMutableArray прохождение по памяти параметров утечки

Я опытный разработчик по Я новичок в Objective-C/iPhone/Какао.

Я хочу создать контроллер класса, который я могу передать NSMutableArray в качестве параметра.

Тогда мы имеем:

selTimeIntController = [[SingleSelectPickerViewController alloc] initWithSettings: listOfIntervals :kAlarmIntervalStr :myDataHolder.alarmInterval]; 
[self.navigationController pushViewController: selTimeIntController animated: YES]; 
[selTimeIntController release]; 

где это listOfIntervals является уже Alloc/инициализации NSMutableArray *.

на моем SingleSelectPickerViewController, мы имеем:

-(id)initWithSettings:(NSMutableArray*)sourceArray :(NSString*)viewCurrentValue :(NSString*)viewTitle { 

    if(self = [self initWithNibName: kNibName bundle: [NSBundle mainBundle]]) { 

      listOfIntervals = [NSMutableArray arrayWithArray: (NSMutableArray*)sourceArray]; 
      currentValue = [[NSString alloc] initWithString: viewCurrentValue]; 
      title   = [[NSString alloc] initWithString: viewTitle]; 
    } 

    return self; 
} 

Через отладки я могу видеть, как мои listOfIntervals создается на моем SingleSelectPickerViewController.

Здесь мы имеем SingleSelectPickerViewController»dealloc:

- (void)dealloc { 
    [super dealloc]; 

    [listOfIntervals release]; 
    [currentValue release]; 
    [title   release]; 
} 

Но, каждый раз, когда я создаю экземпляр моего SingleSelectViewController, я получаю сразу после этого в EXEC_BAD_ADDRESS со следующим стеком:

#0 0x96132688 in objc_msgSend() 
#1 0x00003ee2 in -[SingleSelectPickerViewController tableView:numberOfRowsInSection:] (self=0xd38940, _cmd=0x319a6bc0, tableView=0x102e000, section=0) at /Users/Cadu/iPhone/myApp/Classes/SingleSelectPickerViewController.m:115 
#2 0x30a86bb4 in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:]() 
#3 0x30a8879b in -[UITableViewRowData rectForFooterInSection:]() 
#4 0x30a883c7 in -[UITableViewRowData heightForTable]() 
#5 0x3094e8e6 in -[UITableView(_UITableViewPrivate) _updateContentSize]() 
#6 0x30940a7d in -[UITableView noteNumberOfRowsChanged]() 
#7 0x3094a2a0 in -[UITableView reloadData]() 
#8 0x30947661 in -[UITableView layoutSubviews]() 
#9 0x00b41d94 in -[CALayer layoutSublayers]() 
#10 0x00b41b55 in CALayerLayoutIfNeeded() 
#11 0x00b413ae in CA::Context::commit_transaction() 
#12 0x00b41022 in CA::Transaction::commit() 
#13 0x00b492e0 in CA::Transaction::observer_callback() 
#14 0x30245c32 in __CFRunLoopDoObservers() 
#15 0x3024503f in CFRunLoopRunSpecific() 
#16 0x30244628 in CFRunLoopRunInMode() 
#17 0x32044c31 in GSEventRunModal() 
#18 0x32044cf6 in GSEventRun() 
#19 0x309021ee in UIApplicationMain() 
#20 0x000020d8 in main (argc=1, argv=0xbffff0b8) at /Users/Cadu/iPhone/MyApp/ 

Любая идея о том, что происходит на?

ответ

0

Я новичок в программировании на Mac, но я думаю, что ваш метод dealloc находится в неправильном порядке.

Оно должно быть:

- (void)dealloc { 
    [listOfIntervals release]; 
    [currentValue release]; 
    [title   release]; 

    [super dealloc]; 
} 

Вы должны исправить это, Altough я не думаю, что это решит вашу проблему.

Кроме того, я не понимаю, что вы делаете здесь:

if(self = [self initWithNibName: kNibName bundle: [NSBundle mainBundle]]) { 
    //... 
} 

Я думаю, что должно быть:

if (! [super initWithNibName: kNibName bundle: [NSBundle mainBundle]]) { 

     return nil; 
} 

//... 
+1

Ерунда, [супер dealloc] всегда должен быть последним в -dealloc, иначе вы будете очень вероятно, аварии. – bbum

+1

Это имеет значение. После запуска [super dealloc] пространство, в котором хранятся ваши экземплярные переменные, освобождается, поэтому * что-либо * внутри класса является недопустимым. – Chuck

+0

Не то, чтобы этот конкретный ответ устранил проблему. – bbum

0
  1. Что о sourceArray - это сохраняла где-нибудь? Вы должны сохранить выделенный объект, чтобы использовать его за пределами его объема, иначе он будет автореализован. Вы можете использовать простую ссылку на него в своем классе, не создавая массив снова.

Или, вы можете

listOfIntervals = [[NSMutableArray arrayWithArray: (NSMutableArray *) sourceArray] сохраняют];

, а затем использовать его и отпустить.

  1. Ответ выше отметил, что вы вызываете [super dealloc] перед тем, как освободить все распределения классов. [super dealloc] должен быть вызван в конце.

  2. Существует множество полезных ссылок о управлении памятью какао, особенно об использовании функций alloc/keep. Это действительно важная часть программирования Cocoa/iPhone. Смотрите эту, например: Memory management in Cocoa или просто Google для него

Надеется, что это помогает, удачам

0

Вы должны вернуться к основам на этом.


Проблема 1: Вы огибают изменяемый объект фундаментной.

Это почти всегда свидетельствует о плохом дизайне. Посмотрите на Cocoa/CocoaTouch, и вы увидите очень мало использования изменяемых классов, передаваемых в качестве параметров или возвращенных. Это почти всегда связано с ограничениями производительности.

Почему это плохо? Поскольку после совершения вызова довольно легко получить 2 или более объектов, которые используют один и тот же изменяемый объект. Если кто-то вносит в него изменения, другие не знают об этом, потенциально приводя к довольно странным поведением дальше по линии. НЕ весело отлаживать.


Проблема 2: Вы не сохраняя массив

Это управление памятью фундаментальной и вы обязательно должны понимать, прежде чем пытаться копать себе слишком много. Возможно, Apple может объяснить это лучше, чем я, но это сводится к следующему:

Если вам нужен доступ к объекту, находящемуся за пределами области действия, где вы были предоставлены, сохраните его. Отпустите его, когда вы закончите позже.

Так что вы делаете здесь Назначив autoreleased массива в переменный экземпляре listOfIntervals, и, конечно, она будет выпущен & высвобождена позже, взорвав ваше приложение, когда вы в следующий раз попытаться получить к нему доступ. Вместо этого, вот правильный код:

- (id)initWithIntervals:(NSArray *)sourceArray 
      currentValue:(NSString *)viewCurrentValue 
        title:(NSString *)viewTitle 
{ 
    if (self = [self initWithNibName:kNibName bundle:nil]) 
    { 
    listOfIntervals = [sourceArray mutableCopy]; 
    currentValue = [viewCurrentValue copy]; 
    title   = [viewTitle copy]; 
    } 

    return self; 
} 

Очки примечания:

  • Этот метод назван правильно. Он имеет четкие имена для каждого аргумента.
  • Не нужно звонить [NSBundle mainBundle]. Как указывается в документации, NSViewController сам это определит.
  • Все приведенные аргументы: скопировано. Это имеет два важных последствия:
    • NSArray и NSString являются объекты значение. То есть ваш код интересуется их значением, а не самим объектом. Какао прекрасно позаботится о том, чтобы сделать это максимально эффективным.
    • -copy и -mutableCopt возвращают объекты с сохранением +1, поэтому они не будут никуда идти, пока вы их не отпустите. Идеально подходит для типичной переменной экземпляра.
+0

Майк, спасибо, что указал на материал NSMutableArray x NSArray. Вы правы и, действительно, мой дизайн был неправильным. – Cadu

7

Название вопроса говорит "утечка памяти". Все в вопросе указывает «crasher». Это crasher, а не утечка памяти. Или, по крайней мере, вы не узнаете, есть ли у вас утечка памяти или нет, пока вы не исправите краши.

Наиболее вероятным источником сбоя является неправильное управление переменной экземпляра listOfIntervals.

listOfIntervals = [NSMutableArray arrayWithArray: (NSMutableArray*)sourceArray]; 

В частности, это должно быть:

listOfIntervals = [[NSMutableArray arrayWithArray: sourceArray] retain]; 

Как Майк указано выше, проходя вокруг изменяемой ссылки сбора, вероятно, плохая идея. Что произойдет, если sourceArray изменится из-под вашего класса? Готовы ли вы справиться с этим?

Более распространенная идиома бы объявить метод, как принимая NSArray *, а затем скопировать массив:

listOfIntervals = [sourceArray mutableCopy]; // or -copy, if you don't need it to be mutable 

(1) (NSMutableArray*) броска был ненужным. Знал вред, но зачем ему, если он не нужен?

(2) Вам необходимо сохранить listOfIntervals. + arrayWithArray: создаст автореализованный массив и, таким образом, массив будет released после инициализации объекта, что приведет к сбою, который вы видите.

(3) -copy & -mutableCopy возвращает сохраненные объекты, не нужно вызывать -получить.

Однако, вы также должны установить свой метод -dealloc:

- (void)dealloc { 
    // move this [super dealloc]; 

    [listOfIntervals release]; 
    [currentValue release]; 
    [title   release]; 
    [super dealloc]; // to here 
} 

[super dealloc]всегда должен быть последним. В -dealloc нет ничего волшебного, и, таким образом, имея этот вызов первым, вы сообщали экземпляру освободить себя от , а затем, пройдя и очистив переменные экземпляра. Это привело бы к второму крушению (или неожиданному поведению).

В целом, я предлагаю вам перечитать руководства по управлению памятью.

http://developer.apple.com/iPhone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

+0

Отличный ответ; он не только прав, но и заслуживает внимания для заботы и внимания к деталям в одиночку. –

+0

Хороший ответ. Я хотел бы добавить, что, как правило, вы можете сопоставить, как настроить переменные экземпляра в вашем init-методе с тем, как вы объявите ту же переменную экземпляра, что и @property. сохранить do 'ivar = [obj сохранить]', скопировать do 'ivar = [obj copy]', назначить do 'ivar = obj'. Затем в dealloc выполните '[ivar release]' только для сохранения и копирования резервных копий @property. – PeyloW

+0

Лучший ответ. Спасибо, у меня 5 лет + на материалах C/C++, и я никогда не видел какой-либо SO-деликатной обработки памяти. Это хорошо со стороны, но и раздражает. Я перечитываю руководства по управлению памятью, и теперь я использую инструменты, чтобы гарантировать, что все в порядке. Спасибо за помощь =) – Cadu

Смежные вопросы