пятница, 22 октября 2010 г.

IBOutlet. Инструкция по применению.

На первых порах изучения программирования под iPhone (и чтения соответствующих мануалов), часто встает вопрос, где использовать ключевое слово IBOutlet — при объявлении переменной (способ 1) или при объявлении свойства (способ 2). То есть так:

    @interface MyClass: NSObject {     // способ 1
        IBOutlet UIView *smth; 
    }

или так:

    @interface MyClass: NSObject {     // способ 2
        UIView *smth;
    }
    @property (nonatomic, retain) IBOutlet UIView* smth;

На самом деле тут все просто. Можно использовать оба подхода. Они имеют следующие различия.

В способе 1, когда загрузится nib-файл, создастся и свяжется с переменной соответствующий объект (естественно, если в Interface Builder связь была установлена), он будет "удержан" (как еще перевести retain??) — то есть программист становится ответственным за его освобождение (release) в dealloc-методе.

Сразу оговорюсь — указанное выше справедливо только для iOS, так как в OS X при таком подходе объект просто присваивается переменной (assign) и освобождать его впоследствии не требуется (подробнее о данных процессах, тут: Developer.Apple.Com).

В способе 2 все зависит от указанных у @property параметров. В данном примере явно указан retain, а значит, программист должен будет освободить память впоследствии (обычно — release в dealloc-методе).

Плюс — о чем вы скорее всего догадались и так — в первом случае доступа к переменной извне не будет. Во втором, естественно, все в порядке. Вопрос, нужен ли он. Моя практика убеждает меня, что далеко не всегда. Именно потому, способ 1 тоже имеет право на жизнь (я пишу «тоже» потому, что в оффдоках Apple в 99,9% я видел именно 2 способ, чего не скажешь о примерах [examples] на их сайте).

Теперь небольшие оговорки по способам.

1. Конечно, в 1 варианте вы можете в нагрузку описать еще и property:

    @interface MyClass: NSObject {
        IBOutlet UIView *smth; 
    }
    @property (nonatomic, retain) UIView* smth;

Но как по мне — это выглядит несколько нелепо с точки зрения восприятия (хотя работать по-прежнему будет). С другой стороны, например, в книге iPhone Application Development For Dummies автор использует именно этот способ (что меня несколько удивляет).

Кстати, чисто теоретически, можно  добавить IBOutlet и к полю класса, и к свойству. Работать будет (ни analyzer, ни instruments вроде явных косяков не нашли, хотя allocations не проверял — авось потекло и никто не заметил))). Все из-за того, что сначала Interface Builder ищет IBOutlet среди свойств и только потом — среди полей класса. Но опять же (по моему очень скромному мнению) — лишняя городулька не способствует приятности чтения кода.

2. Во 2 варианте, если вы будете генерировать методы с помощью @synthesize, можно обойтись без поля класса — Objective-C 2.0 легко сгенерирует его для вас:

    @interface MyClass: NSObject {
    }
    @property (nonatomic, retain) IBOutlet UIView* smth;

Однако и всю работу вам придется вести через свойство, а не через переменную (smth уже не сработает, придется писать self.smth со всеми отовсюду вытекающими). Однако сейчас большинство зарубежных разработчиков (раз уж мы об этом заговорили) практикуют подход с явным указанием поля, причем с другим именем (начинающимся с подчеркивания). То есть так:

    @interface MyClass: NSObject {
        UIView *_smth;
    }
    @property (nonatomic, retain) IBOutlet UIView* smth;

а потом так:

    @synthesize smth = _smth;

Мотивируется это тем, что тогда человек не запутается (и сразу сможет увидеть), где поле, а где — свойство. Больших аргументов я пока не знаю (кто подскажет — того буду любить вечно!), но вроде как эдакий Objective-C Best Practice формируется...

Вот как-то так...

1 комментарий: