Переменная не 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. Пожалуйста, дай мне руку! Я схожу с ума! Программа настолько проста, что я очень нервничаю из-за проблем в будущем кодировании...


person Karen Fisher    schedule 19.04.2011    source источник
comment
Я настоятельно рекомендую использовать класс NSMutableArray, карты [j] выглядят по-домашнему.   -  person Nick Weaver    schedule 19.04.2011


Ответы (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, также освобождается.

person Community    schedule 19.04.2011
comment
Bavaria, спасибо большое! Теперь все работает нормально. Кстати, а как стать владельцем строковой формы [cardlist substringWithRange:NSMakeRange(i,2)]], если я использую простую переменную NSString*? Использовать retain? Я имею в виду: NSString * temp; ... temp=[подстрока списка карточекWithRange:NSMakeRange(i,2)]; [временное сохранение]; Я прав? И что я должен прочитать, чтобы узнать обо всех этих вещах, которые вы знаете? :-) - person Karen Fisher; 19.04.2011
comment
@yyk Если это только локальная/автоматическая переменная, т. е. та, которая используется только в определенном методе, то вам обычно не нужно брать на себя ответственность за нее. Эти переменные будут действительны на протяжении всего метода. Но да, если вам нужно стать владельцем этой строки, используйте либо -copy, либо -retain. Вам следует прочитать Руководство по программированию управления памятью для более подробного обсуждения управления памятью. - person ; 20.04.2011