Почему неявная инициализация переменной не работает стабильно на iPhone?

#iphone #objective-c #initialization #init #alloc

#iPhone #objective-c #инициализация #alloc

Вопрос:

Итак, вот мой сценарий — в заголовочном файле класса, который я делаю:

 @interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}

- (void) methodOne: (NSString *) passedString;
- (void) methodTwo: (NSString *) passedString;

@end
  

В файле реализации я делаю это:

  #import MyClass.h

    @implementation MyClass

    - (void) methodOne: (NSString *) passedString
    {
       NSLog(@"%@", passedString);
       string1 = passedString;
    }

    - (void) methodTwo: (NSString *) passedString
    {
       NSLog(@"%@", passedString);
       string2 = passedString;
    }
  

Я обнаружил, что при выполнении этого есть какая-то несогласованность по сравнению с [NSString alloc] initWithString:] .

Как вы можете видеть, string1 и string2 обрабатываются точно так же, но происходит то, что string1 устанавливается, но string2 остается пустым. Я получаю плохой доступ, когда ссылаюсь на него позже.

Я подумал, что, возможно, я передаю пустую строку в methodTwo: поэтому я добавил этот NSLog, который доказывает, что он не пустой, но имеет ожидаемую строку.

Поскольку я заметил это несоответствие до того, как решил перейти на это:

         - (void) methodOne: (NSString *) passedString
        {
           NSLog(@"%@", passedString);
           string1 = passedString;
        }

        - (void) methodTwo: (NSString *) passedString
        {
           NSLog(@"%@", passedString);
           string2 = [[NSString alloc] initWithString: passedString];
        }
  

Теперь обе строки работают должным образом. Мой вопрос в том, почему существует это несоответствие?

Это не единственный раз, когда это случилось со мной. Это происходило со всеми видами объектов. Единственное, что, кажется, работает каждый раз, — это alloc init . Такие методы, как stringWithString: работают большую часть времени, но не всегда.

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

1. Можете ли вы указать вызывающий код? Это тесно связано с тем, как создается passedString и, следовательно, его счетчик ссылок.

Ответ №1:

Это потому, что в первом примере вы не сохраняете и не копируете строки. string2 освобождается в какой-то момент, прежде чем вы его используете. На самом деле это чистая удача, что string1 в порядке. Вы должны изменить свой код, чтобы он был примерно таким:

 - (void) methodOne: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   NSString* oldString = string1;
   string1 = [passedString copy];
   [oldString release];
}

- (void) methodTwo: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   NSString* oldString = string2;
   string2 = [passedString copy];
   [oldString release];
}
  

и освобождение в dealloc

 -(void) dealloc
{
    [string1 release];
    [string2 release];
    // other stuff
    [super dealloc];
}
  

Я настоятельно рекомендую вам создать свойства для string1 и string2 для обработки всего этого подсчета ссылок:

 @interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}

- (void) methodOne: (NSString *) passedString;
- (void) methodTwo: (NSString *) passedString;

@property (copy) NSString* string1;
@property (copy) NSString* string2;  

@end

@imlementation MyClasss
@synthesize string1, string2;

- (void) methodOne: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   [self setString1: passedString];
}

- (void) methodTwo: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   [self setString2: passedString];
}

// dealloc as before

@end
  

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

1. Имеет смысл! Однако один вопрос: мне не нужно освобождать их в dealloc при правильном использовании свойств? Или я должен, поскольку для этих свойств установлено значение копировать?

2. Все равно нужно освободить в dealloc, даже для свойств, которые сохраняются. Вместо этого вы могли бы установить для них значение nil, т.е. [self setSetString1: nil] Но это не рекомендуется в `dealloc.

Ответ №2:

Вы допускаете ошибки управления памятью. Вы должны сохранять или копировать строки, когда вы назначаете их своим ivars (и освобождаете их позже).

Возможно, вы все еще можете получить доступ к объекту, даже если он был освобожден, когда занимаемая им память еще не была перезаписана. Но вы не можете полагаться на это.

Ответ №3:

Если переданные строки автоматически освобождаются, при их назначении сохранение не выполняется. Постоянные строки (@str») по существу никогда не освобождаются, созданные строки, такие как stringWithFormat, должны быть сохранены.

Пожалуйста, покажите вызывающим.

Использование @properties с сохранением устранит многие проблемы с сохранением. Или рассмотрите возможность использования ARC, что устраняет необходимость сохранения / освобождения / автоматического выпуска.

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

1. =] Не беспокойтесь. Просто бросилось в глаза.

Ответ №4:

Как уже говорилось в этой теме, у вас возникли некоторые проблемы с управлением памятью, возможно, вы не совсем понимаете, как выделяются и сохраняются NSObjects, вам следует ознакомиться с Objective-C управление памятью. В то же время есть два подхода, которые вы могли бы предпринять для решения вышеуказанной проблемы.

Вы могли бы сохранить свои переменные-члены NSString (string1 amp; string2) как свойства вашего класса, помимо некоторых других функций, объявление которых как свойств дало бы им средства доступа setter и getter, которые вы бы вызывали вместо method1 и method2. Таким образом, это изменит ваш код, чтобы он выглядел следующим образом в вашем заголовочном файле

 @interface MyClass : NSObject
{
     NSString *string1;
     NSString *string2;
}

@property( nonatomic, retain)NSString* string1;
@property( nonatomic, retain)NSString* string1;
  

Затем не забудьте добавить следующее в исходный файл (обычно в верхней части файла после строки @implementation MyClass)

 @implementation MyClass
@synthesize string1;
@synthesize string2;
  

Затем в классе, из которого вы вызывали method1 и method2, вы можете изменить код, чтобы он выглядел следующим образом

     //Lets assume somewhere you've called an init Method for your MyClass Object, something like
    MyClass* myClassObject = [[MyClass alloc] init];

//you can then call the setters to set the string like so
[myClassObject setString1:@"some string"]; //or myClassObject.string1 = @"some string";
[myClassObject setString2:@"some string"]; //or myClassObject.string2 = @some other string";
//Calling these member variables either of the above ways is valid, I prefer the former as it's a bit easier on the eye for me

//and to get the values back out of the strings you could call
NSString* output = [myClassObject string1];
//or
NSString* output2 = myClassObject.string2;
  

Теперь вы можете по какой-то причине не захотеть использовать свойство @ для переменных-членов NSString, поэтому вы можете изменить свой исходный исходный файл (.m), чтобы он выглядел как

 @implementation MyClass

- (void) methodOne: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   if( string1 != nil )
   {
      [string1 release];
   }
   string1 = [[NSString alloc] initWithString:passedString];
}

- (void) methodTwo: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   if( string2 != nil )
   {
      [string2 release];
   }
   string2 = [[NSString alloc] initWithString:passedString];
}
  

Это должно решить проблему, почему ваши строки недействительны, поскольку вы не будете перезаписывать память и пытаться считывать мусор таким образом. Вам нужно будет не забыть освободить эти NSStrings в вашем dealloc, если они не равны нулю, иначе вы тоже получите утечку памяти.

Надеюсь, это поможет.