Переменная, не являющаяся CFString

#objective-c #cfstring

#objective-c #cfstring

Вопрос:

Я кодирую на Ojective-C всего месяц и захожу в тупик. Нужна помощь. Вот история:

  1. У меня есть простой класс LXPPlayingCard:

      #import <Cocoa/Cocoa.h>
     @interface LXPPlayingCard : NSObject {
     @private NSString* cardCV;
     @private int position;
     }
    
     @property (readwrite, assign) NSString* cardCV;
     @property (readwrite,assign) int position;
     @end
    
     @implementation LXPPlayingCard
     @synthesize cardCV;
     @synthesize position;
     @end
      
  2. Также у меня есть едва ли более сложный класс LXPDeck:

     #import <Cocoa/Cocoa.h>
    #import "LXPPlayingCard.h"
    @interface LXPDeck : NSObject {
    LXPPlayingCard* cards[100];
    int deckCapacity;
    }
    
    -(void) fill:(NSString *) cardlist;
    -(void) showList;
    -(int) deckCapacity;
    
    @end
    
    #import "LXPDeck.h"
    #import "LXPPlayingCard.h" 
    @implementation LXPDeck
    -(void) fill:(NSString *) cardlist {
        int l,i,j;
        l=[cardlist length];
        j=0;
        for (i=0;i<l;i =2) {
            cards[j]=[[LXPPlayingCard alloc] init] ;        
            [cards[j] setCardCV:[cardlist substringWithRange:NSMakeRange(i,2)]];
            [cards[j] setPosition:j 1];
            NSLog(@"%@",[cards[j] cardCV]);
            j  ;
        }
      deckCapacity=j;
      }
    
      -(int) deckCapacity { return deckCapacity;}
    
      -(void) showList {
      NSLog(@"deck capacity:%d",deckCapacity);
      NSString * temp;
      temp=[[NSString alloc] init];
      for (int i=0;i<deckCapacity;i  ) {
        NSLog(@"card[%d]=%d, adress:%p",i,[cards[i] position],cards[i]);
        temp=[cards[i] cardCV];
        NSLog(@"%@",temp);
        }
         }
    
     @end
      

которая выполняет пару действий: заполняет массив карт именами (setCardCV) из строки и печатает содержимое колоды (showList).
Далее я создаю класс AppController:

 @interface LXPAppController : NSObject {

}
-(IBAction) openNewDeck:(NSButton * )sender;
-(IBAction) printDeckContent:(NSButton *)sender ;

@end

#import "AppController.h"
#import "LXPDeck.h"


@implementation LXPAppController
    BOOL deckOpened=FALSE;
    LXPDeck* workDeck;

-(IBAction) openNewDeck:(NSButton *) sender{

    if (!deckOpened) {
        NSLog(@"Opening new deck...");
        workDeck=[LXPDeck alloc];
        [workDeck fill:@"pacataca"];
    }

    deckOpened=TRUE;
}
-(IBAction) printDeckContent:(NSButton *) sender {

    if (deckOpened) {
        NSLog(@"Printing deck content...");
        [workDeck showList];
    }
}
@end
  

и две кнопки в главном окне, с которыми я связался, openNewDeck и printDeckContent методы.
Проблема в том, что приложение падает с ошибкой «EXC_BAD_ACCESS», и это происходит при i = 3, потому что (когда я использую debugger) [card[i] cardCV] не является CFString.

Я пытался заполнить колоду разными строками, и иногда программа завершалась сбоем на первом круге отображения [cards[i] cardCV] . Я действительно не понимаю, что происходит, но я предполагаю, что это как-то связано с указателями и правилами распределения памяти, потому что нет проблем с простыми типами данных ( position например) и showList метод работает должным образом, если он вызывается из fill метода. Пожалуйста, помогите мне! Я схожу с ума! Программа настолько проста, что я действительно нервничаю из-за проблем в будущем кодировании…

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

1. Я настоятельно рекомендую использовать класс NSMutableArray, cards[j] выглядят как c-ish.

Ответ №1:

Проблема в том, что

 [cardlist substringWithRange:NSMakeRange(i,2)]]
  

возвращает NSString , которой вы не владеете. Поскольку она вам не принадлежит, нет гарантии, что строка будет действительна в течение всего срока службы вашего LXPPlayingCard объекта. Если вы хотите сохранить действительную ссылку на эту строку, вам следует изменить объявление LXPPlayingCard так, чтобы cardCV свойство стало copy свойством, а не assign единицей. Заменить:

 @property (readwrite, assign) NSString* cardCV;
  

с:

 @property (readwrite, copy) NSString* cardCV;
  

Делая это, когда вы отправляете -setCardCV: подобное:

 [cards[j] setCardCV:[cardlist substringWithRange:NSMakeRange(i,2)]];
  

setCardCV скопирует ее аргумент, [cardlist substringWithRange:NSMakeRange(i,2)] и ваш объект получит право собственности на эту строку. Это означает, что строка будет оставаться действительной на протяжении всего жизненного цикла этого объекта. Помните, что поскольку вы стали владельцем этой строки, вы несете ответственность за ее освобождение. Следовательно, вам необходимо реализовать -dealloc метод:

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

Это означает, что когда соответствующая LXPPlayingCard освобождается, строка, хранящаяся в cardCV , также освобождается.

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

1. Бавариус, большое спасибо! Теперь все работает нормально. Кстати, как стать владельцем строковой формы [cardlist substringWithRange:NSMakeRange(i,2)]] , если я использую простую переменную ‘NSString *’? Использовать retain ? Я имею в виду: NSString * temp; … temp=[cardlist substringWithRange:NSMakeRange(i, 2)]; [temp сохранить]; Я прав? И что я должен прочитать, чтобы узнать обо всем этом, что вы знаете? 🙂

2. @yyk Если это всего лишь локальная / автоматическая переменная, то есть такая, которая используется только в определенном методе, то вам обычно не нужно становиться владельцем этой переменной. Эти переменные будут действительны на протяжении всего метода. Но да, если вам нужно стать владельцем этой строки, используйте либо -copy , либо -retain . Вам следует прочитать Руководство по программированию управления памятью для более подробного обсуждения управления памятью.