Управление памятью iPhone (специально для собственности) - PullRequest
1 голос
/ 24 декабря 2009

У меня очень четкий вопрос:

//.h file

@property (nonatomic, retain)NSMutableString * retainString;
@property (nonatomic, copy)NSMutableString * copyString;

//.m file
@synthesis retainString, copyString;
-(void)Process
{
  NSMutableString *test = [[NSMutableString alloc]inti];//retain count should be 1

  self.retainString = test;

  self.copyString = test;

}

конд. 1-> // retain count должен быть равен 2. Так как они указывают на одну и ту же ячейку памяти с retain count 2, то, как может быть записано освобождение.

конд. 2-> // // сохраняем количество тестов равным 1, а copyString равным 2. Поскольку оба хранят разные области памяти. но можем ли мы написать [релиз copyString].

Ответы [ 3 ]

9 голосов
/ 24 декабря 2009

Эта настройка на самом деле делает некоторые очень интересные вещи и поднимает пару хороших моментов об управлении памятью Objective-C. Давайте сначала повторим код:

// Testing.h
@interface Testing : NSObject {
    NSMutableString *retainString;
    NSMutableString *copyString;
}

@property(nonatomic,retain) NSMutableString *retainString;
@property(nonatomic,copy) NSMutableString *copyString;
// Testing.m
@implementation Testing

@synthesize retainString, copyString;

- (id)init {
    if(self = [super init]) {
        NSMutableString *test = [[NSMutableString alloc] init];
        NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
        self.retainString = test;
        NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
        self.copyString = test;
        NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
        [self.copyString appendFormat:@"test"];
        NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
    }
    return self;
}

@end

Это приводит к выводу журнала:

2009-12-24 03:35:01.408 RetainCountTesting[1429:40b] test 1; retain 0; copy 0
2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 0
2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 2147483647
2009-12-24 03:35:01.413 RetainCountTesting[1429:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendFormat:'

Так что здесь происходит? Первые два звонка довольно просты:

  • При первоначальном вызове alloc / init создается новый объект NSMutableString с сохранением количества 1, как и ожидалось. У нас есть один объект с одним удержанием на нем.
  • Присвоение свойству retain ed увеличивает ожидаемый счетчик. У нас есть один объект с двумя удержаниями на нем.

Вот где это становится странным. Присвоение свойству copy действительно делает копию, но не так, как вы ожидаете. NSString и NSMutableString являются частью того, что называется кластер классов - когда вы создаете или изменяете строку, она может быть или не быть экземпляром ожидаемого класса. Язык может мутировать его в какое-то другое закулисное представление.

В этом конкретном случае, когда копирование выполняется, очевидно, язык решает, что строка (так как она не содержит информации) должна считаться неизменной, и делает это так. Это часто наблюдается, когда люди делают что-то вроде [[NSString alloc] initWithString:@"hello"] - это постоянная статическая строка, поэтому никакой динамический выделение объекта не требуется. Сохранение статичности помогает среде выполнения работать лучше.

Итак, теперь у нас есть два объекта: наш исходный объект test, который был сохранен дважды, и новый объект, который является статическим и поэтому имеет счет сохранения INT_MAX. Наконец, поскольку новая строка является неизменной, вызов метода-мутатора убивает программу.

Кроме того, изменение исходного вызова с init на initWithString: действительно приводит к тому, что назначение копирования выполняется (несколько), как и ожидалось - вы получаете только счет сохранения 1 для скопированного объекта, но вы все равно не можете мутировать его. Опять же, это, вероятно, связано с некоторой оптимизацией внутри компилятора, который решил, что строка является статической, и не видел причин делать ее изменяемой, если это не нужно.

Чтобы ответить на ваш последний вопрос: да , вы можете позвонить release на любой из этих объектов. Это просто не поможет. В лучшем случае вы уничтожите скопированный объект (так как он имел счет сохранения 1); в худшем случае это ничего не даст статическому строковому объекту. Однако я бы порекомендовал продолжать работать со свойствами: вместо того, чтобы освобождать скопированный объект, почему бы просто не сделать self.copyString = nil;? Так как он вызывает установщик свойств, он позаботится о выпуске по мере необходимости, и тогда у вас нет указателя на объект, все еще плавающий вокруг.

Для получения дополнительной информации обо всем этом, прочитайте:

1 голос
/ 24 декабря 2009

Если у вас есть свойства, определенные с ключевыми словами 'retain' или 'copy', вы всегда должны освобождать соответствующие переменные-члены в методе dealloc.

В этом случае ваш dealloc должен выглядеть так:

- (void)dealloc {
    [retainString release];
    [copyString release];

    [super dealloc];
}

Теперь, когда вы alloc строка в методе вашего пользовательского класса, этот метод владеет строкой, как описано в Руководство по программированию управления памятью для какао . Это означает, что вы должны release строку перед выходом из метода.

@synthesize retainString, copyString;

- (void)Process {
    NSMutableString *test = [[NSMutableString alloc] init]; //retain count is 1

    self.retainString = test;     // retain count of test is 2
    self.copyString = test;       // test's retain count = 2, copyString's = 1

    [test release];               // retain count of test is 1 again
}

Когда экземпляр этого класса уничтожается, будет вызван метод dealloc, который в свою очередь release вызовет обе строки. И как только у них останется 1, они тоже будут dealloc'ed.

0 голосов
/ 24 декабря 2009
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...