Сохраняйте, выделяйте, свойства ... Тема, чтобы сделать вашу жизнь Obj-C проще! - PullRequest
5 голосов
/ 23 марта 2010

Чем больше я кодирую, тем больше я теряюсь ... поэтому я решил создать тему, полностью посвященную управлению памятью, для меня (и других), чтобы не тратить часы на понимание основ obj-c ... Я буду обновите его по мере поступления новых вопросов!

Хорошо, вот несколько примеров:

// myArray is property (retain)
myArray = otherArray;

//myArray isn't a property
myArray = otherArray;

//myArray is a property (retain)
myArray = [[NSArray alloc] init];

//myArray isn't a property
myArray = [[NSArray alloc] init];

--- Итак, если я понимаю ... когда вы помещаете self.myArray, вы говорите Xcode использовать getter или setter, но когда вы просто делаете myArray, вы отвечаете за все, верно?

[решено] ОБНОВЛЕНИЕ1 : Есть ли разница между:

//myArray is a property
myArray = otherArray; // it is only a reference, releasing otherArray will imply releasing myArray
self.myArray = otherArray; // otherArray is sent a retain message so releasing otherArray will still keep myArray in memory

--- Да, есть разница (см. Комментарии выше)

[решено] ОБНОВЛЕНИЕ 2: Значение myArray ниже равно нулю?

NSArray *myArray;

--- Куби: Да, он равен нулю.

[решено] ОБНОВЛЕНИЕ3: считается ли оно за 2 удержания? Один удерживает от себя, а другой удерживает от выделения? Это утечка памяти?

self.myArray = [[NSArray alloc] init];

--- Куби: Да, это утечка памяти!

[решено] ОБНОВЛЕНИЕ 4: собственность обо всем позаботится? нет необходимости выделять или освобождать?

self.myArray = [NSArray array];

--- Мы используем установщик так, чтобы массив сохранялся правильно

[решено] ОБНОВЛЕНИЕ 5: Эти два блока одинаковы?

//myArray is a retained property

self.myArray = [NSArray array]; //retain
self.myArray = nil; //release and set to nil

myArray = [[NSArray alloc] initWithArray]; //retain
self.myArray = nil; //release and set to nil

--- Куби: Да, они идентичны

Спасибо за время.

Готье.

Ответы [ 3 ]

6 голосов
/ 23 марта 2010

Во-первых, я предполагаю, что у вас есть свойство с именем myArray и iVar с именем myArray? Если это так, случаи 1,2 идентичны, а 3,4 идентичны. Если вам нужно установить свойство вашего текущего класса, вы должны сделать это одним из следующих методов:

self.myArray = otherArray;
[self setMyArray:otherArray];

строка myArray = otherArray будет устанавливать только iVar, но не свойство.

Вторая часть, вы спрашиваете об управлении памятью. Шаг первый: прочитайте Руководство Apple . Это действительно требуется чтение. Не беспокойтесь, если вы не совсем понимаете, продолжайте читать его раз в месяц, и в конце концов он кристаллизуется.

Шаг второй: запомните это правило: если вы alloc, copy, new или retain объект, вы несете ответственность за освобождение этого объекта, если вы этого не сделаете, он будет утечка.

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

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


Обновление 1 Очень большая разница. Две строки полностью отличаются. В синтаксисе точек важно понимать, что эти две строки в точности эквивалентны:

self.myArray = otherArray;
[self setMyArray:otherArray];

Обратите внимание, что вторая строка - это вызов метода. Теоретически вы можете поместить все, что вы хотели в этом методе. Вы можете установить myArray на ноль, или установить someOtherArray, или обновить твиттер или что-то еще.


Обновление 2 Да, указатели в Obj-C инициализируются в ноль.


Обновление 3 Именно так. Если свойство myArray объявлено как retain и вы используете синтезаторы по умолчанию, это приведет к утечке памяти.

Обновление 5 Точно так же.

5 голосов
/ 23 марта 2010

Хороший ответ Куби.Перечитывая Руководство Apple, пока вы не начнете это делать, это действительно обязательно.

В то же время вы можете воспользоваться этим строгим набором правил, которым я следую, чтобы избежать случайных ошибок памяти.Они предписывают чуть больше печатать, чем это строго необходимо, и они, вероятно, немного менее эффективны во время выполнения, чем могли бы быть, но последовательное соблюдение этих правил защитит вас от наиболее распространенных ошибок управления памятью.Затем, когда вам станет удобнее управлять памятью, вы можете выборочно отклоняться от этих правил, хотя я все еще редко делаю это.

  1. Объявляйте свойство для каждой переменной экземпляра объекта, которую вы создаете.Объявите его как (nonatomic, retain), если это не объект, которому не принадлежит ваш класс, например, делегат, который создал бы циклическую ссылку.В этом случае объявите его как (неатомический, назначьте), чтобы избежать утечки всех объектов в этом цикле.

    @property (nonatomic, retain) NSString *title;
    @property (nonatomic, assign) id <WidgetDelegate> delegate;
    
  2. Даже если переменная экземпляра объектапредназначенный только для частного использования классом, объявите свойство для него в расширении класса в верхней части вашего файла .m, чтобы метод синтезированного сеттера мог позаботиться об управлении памятью.

    // Widget.m
    @interface Widget()
    @property (nonatomic, retain) NSString *privateState;
    @end
    
    @implementation Widget
    @synthesize title, delegate, privateState;
    // ...
    @end
    
  3. Каждый раз, когда вы присваиваете объект переменной экземпляра, всегда устанавливайте его через свойство, используя self.

    self.title = @"Title";
  4. В вашем инструменте установитекаждое свойство объекта в ноль.Если вы следовали приведенным выше методам, это одновременно освободит ваши переменные экземпляра и установит для них значение nil для защиты от EXC_BAD_ACCESS.Сделайте dealloc первым методом в своем классе, чтобы не забыть никаких свойств.

    - (void) dealloc {
        self.title = nil;
        self.delegate = nil;
        self.privateState = nil;
        [super dealloc];
    }
    
  5. Для каждого написанного вами пользовательского класса убедитесь, что в нем есть хотя бы один метод фабрики классов, которыйделегирует метод init с теми же параметрами и автоматически высвобождает возвращаемый объект.Это ограничивает почти все вызовы alloc и init для этих фабричных методов, а не разбрасывает их по всему коду.

    - (id)initWithTitle:(NSString *)theTitle delegate:(id )theDelegate {
        if (self = [super init]) {
            self.title = theTitle;
            self.delegate = theDelegate;
            self.privateState = @"start";
        }
        return self;
    }
    &#043; (id)widgetWithTitle:(NSString *)theTitle delegate:(id )theDelegate {
        return [[[self alloc] initWithTitle:theTitle delegate:theDelegate] autorelease];
    }
    
  6. Всякий раз, когда вы создаете экземпляр объекта, всегда делайте это с помощью метода фабричного класса, есливозможный.Это дает вам автоматически освобожденный объект, поэтому вам не нужно освобождать его, пока вы не сохраните его.

    self.widget = [Widget widgetWithTitle:@"My Widget" delegate:self];
    
  7. Когда вам нужно создать экземпляр объекта, у которого нет соответствующего метода фабричного класса, autorelease это на той же линии, так что вы не забудьте сделать это позже.(Исключение: освободить вручную, если вы делаете это тысячи раз в тесном цикле.)

    self.containerView = [[[UIView alloc] initWithFrame:self.bounds] autorelease]; 
    
  8. Если вы освобождаете объект, имеющий делегат или подобное свойство, котороеуказывает круговую ссылку, сначала установите для этого свойства значение nil.Это предотвращает EXC_BAD_ACCESS в случае, если объект переживает своего делегата и пытается вызвать метод делегата после его освобождения.

    - (void)dealloc {
        self.widget.delegate = nil;
        self.widget = nil;
        self.containerView = nil;
        [super dealloc];
    }
    

Многие опытные разработчики успешно управляют памятью, не следуя всемиз этих практик.Если вы понимаете управление памятью и не склонны иметь ошибки, связанные с памятью, я, безусловно, рекомендую вам придерживаться того, что вам подходит.Но если вы новичок в управлении памятью iPhone или если ваш код страдает от случайных ошибок, связанных с памятью, я надеюсь, что вы найдете эти методы столь же полезными, как и я.

0 голосов
/ 23 марта 2010

Лучшее место, где можно найти ответ на этот вопрос, - в соответствующей документации Apple, которая объясняет все об управлении памятью .Сказав это, вы используете здесь только ivars, вы не настраиваете ivar, используя сгенерированные ObjC сеттеры.Как говорит Куби, вам нужно будет сказать

self.myArray = otherArray;

, чтобы использовать myArray для "свойства" ness.

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