Проблема с памятью для создания динамического объекта Objective-C

#iphone #objective-c #memory-management

#iPhone #objective-c #управление памятью

Вопрос:

Некоторое время я сталкивался со странной проблемой в моей программе для iPhone.

Короче говоря: у меня есть XML, который содержит данные о древовидной композиции. Я написал анализатор, который создает дерево из XML.

Где-то в классе DataSource у меня есть следующий метод:

  - (id)loadAllData {
   if (self.doc != nil) {
    GDataXMLElement *root = [self.doc rootElement];
    Component *component = [Component componentWithRoot:root];
    NSLog(@"%@", component);
    return component;
  }
  return nil;
}
  

Заводской метод выглядит следующим образом:

   (id)componentWithRoot:(GDataXMLNode*)element {
  id<iComponent> component = nil;
  NSString *clsName = [NSString stringWithFormat:@"%@Element", [element name]];

  // Will return instance of a class or |nil| if class is not loaded.
  Class cls = NSClassFromString(clsName);

  if (cls != nil) {
    component = [[cls alloc] init];

    // dies on next line
    NSLog(@"created %@", component);

    // Seems that we have corresponding model class
    if (component != nil) {

      //... setting attrubutes for this model

      //... trying to find child elements and add them to the component instance.
    }
  }
  return component;
}
  

Также, когда я останавливаю GDB в строке, где я вызываю методы alloc и init , и пытаюсь вызвать их в отладчике, я вижу следующий журнал ошибок:

 (gdb) po component

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x828282df
0x01052a67 in objc_msgSend ()
The program being debugged was signaled while in a function called from GDB.
GDB has restored the context to what it was before the call.
To change this behavior use "set unwindonsignal off"
Evaluation of the expression containing the function (_NSPrintForDebugger) will be abandoned.
  

Итак, ситуация такова: каким-то образом моя программа запускается и корректно отображает нужные мне значения. Но когда я пытаюсь что-то сделать со своим component , программа просто вылетает. Есть идеи? Как я могу найти ошибку? Я не знаю никаких причин, по которым NSLog(@"created %@", component); происходит сбой приложения, и поэтому выполняю другие действия с component (на самом деле я хочу закодировать его в архив).

Заранее спасибо!


Обновить

Это фрагмент кода заводского метода с комментариями

   (id)componentWithRoot:(GDataXMLNode*)element {
  id<iComponent> component = nil;
  NSString *clsName = [NSString stringWithFormat:@"%@Element", [element name]];
  NSLog(@"%@", clsName);
  // Will return instance of a class or |nil| if class is not loaded.
  Class cls = NSClassFromString(clsName);
  if (cls != nil) {
    component = [[[cls alloc] init] autorelease];
    // Seems that we have corresponding model class
    if (component != nil) {

      [component setTitle:[element name]]; 
      /*
       when I run app I have first element named ConfigElement. 
       Code runes fine to this point and even properties are set correct.
       But if I set breakpoint at this line and type in consle 
         po component
       I get message which is posted below
      */ 
  

компонент po (gdb)

Программа получила сигнал EXC_BAD_ACCESS, не смогла получить доступ к памяти. Причина: KERN_INVALID_ADDRESS по адресу: 0x828282df 0x01052a67 в objc_msgSend () Во время выполнения функции, вызванной из GDB, был получен сигнал об отлаживаемой программе. GDB восстановила контекст таким, каким он был до вызова. Чтобы изменить это поведение, используйте «set unwindonsignal off», оценка выражения, содержащего функцию (_NSPrintForDebugger), будет отменена.

Поскольку это происходит, я хочу убедиться, что все методы инициализации верны. Вот что я делаю:

сначала проверьте, что имя класса ConfigElement — да

второй взгляд ConfigElement init — все в порядке:

 - (id)init {
  self = [super init];
  return self;
} 
  

третье — посмотрите на super ( Composite ) инициализатор:

 - (id)init {
  if ((self = [super init])) {
    children_ = [[NSMutableArray alloc] init];
  }
  return self;
}
  

затем снова найдите суперинициализатор ( Component теперь инициализатор) — снова кажется хорошим:

 - (id)init {
  if ((self=[super init])) {
    title_ = nil;
    name_ = nil;
    icon_ = nil;
    parent_ = nil;
    children_ = nil;
  }
  return self;  
}
  

Поскольку Component это подкласс NSObject, мне не нужно снова беспокоиться о суперинициализаторе, и, вероятно, я мог бы удалить if инструкцию, но пусть так и будет. Также все эти инициализаторы обозначены, и они фактически вызываются в момент init .

Итак, почему я получаю сообщение об ошибке в GDB?

Комментарии:

1. Что произойдет, если вы сделаете это NSLog(@"component %@", cls) после cls != nil строки? Кроме того, почему вы используете это, а не встроенные инструменты сериализации / архивирования?

2. Здесь происходит утечка памяти. Если ваш заводской метод не вызывается чем-то вроде «создать», «скопировать» или «создать», то он должен возвращать автоматически выпущенный объект. Что касается причины сбоя, я не уверен.

3. @toiletseat Мне нужно использовать это, потому что я не могу хранить объекты в простом списке из-за требований совместимости. Отвечая на ваш вопрос — программа просто умирает. Я не вижу никаких причин, потому что я импортировал соответствующие классы раньше, clsName был установлен правильно, cls также нет null . И когда я делаю cls alloc , не должно быть возвращено ничего, кроме правильного указателя, я прав?

4. @Dig, вы переопределили init метод в классах, которые Component создают экземпляры? Вы определенно возвращаетесь self из метода init? Работает ли это, если вы вводите жесткий код в имени класса, который пытаетесь создать? Мне было бы интересно увидеть init метод класса, который вы пытаетесь создать, который завершается с ошибкой.

5. @Dig, получаемое вами исключение (в objc_msgSend) обычно означает, что вы отправляете сообщение объекту, который был освобожден или фактически не является допустимым объектом (т. Е. int или случайной областью памяти). Ошибка возникает при вызове description метода вашего объекта либо через NSLog , либо через GDB po , я не предполагаю, что вы переопределили description в своей Component иерархии классов? Я скачаю ваш исходный код и посмотрю.

Ответ №1:

Проблема была в моем глючном description методе:

 - (NSString*)description {
  NSString *desc = [NSString stringWithFormat:@"Title: %@, Name: %@, Icon: %@, parent: %@, children: %@", self.title, self.name, self.icon, self.parent];
  return desc;
}
  

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

Ответ №2:

Оба componentWithRoot: и loadAllData должны возвращать autorelease общий объект.

Все, что сказал toiletseat, — это хорошие предложения / вопросы. Это звучит как внутренняя проблема вашего Component класса. Нужно было бы увидеть его внутренности, чтобы сказать больше.

Комментарии:

1. Привет, спасибо за внимание, да, я понимаю, что это, безусловно, мои ошибки реализации, но на данный момент не могу их обнаружить. Разместил мои классы здесь gist.github.com/904194