#objective-c #cfstring
#objective-c #cfstring
Вопрос:
Я кодирую на Ojective-C всего месяц и захожу в тупик. Нужна помощь. Вот история:
-
У меня есть простой класс 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
-
Также у меня есть едва ли более сложный класс 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
. Вам следует прочитать Руководство по программированию управления памятью для более подробного обсуждения управления памятью.