Переменная не 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 потому что (когда я использую отладчик) [card[i] cardCV] не является CFString.

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

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 выпущен тоже.

Другие вопросы по тегам