Можливість розміщувати змінні екземпляра в @implementation
блоці або в розширенні класу - це особливість «сучасного середовища виконання Objective-C», яке використовується у всіх версіях iOS та 64-розрядних програмах Mac OS X.
Якщо ви хочете написати 32-розрядні програми Mac OS X, ви повинні помістити змінні свого екземпляра в @interface
декларацію. Швидше за все, вам не потрібно підтримувати 32-розрядну версію вашого додатка. OS X підтримує 64-розрядні програми з версії 10.5 (Leopard), яка вийшла понад п'ять років тому.
Отже, припустимо, ви пишете лише ті програми, які використовуватимуть сучасний час роботи. Куди слід покласти свої івари?
Варіант 0: у @interface
(Не робіть це)
По-перше, давайте розберемося, чому ми не хочемо поміщати змінні екземпляра в @interface
декларацію.
Поміщаючи змінні екземпляра в @interface
експозицію, ви отримуєте деталі реалізації для користувачів класу. Це може змусити цих користувачів (навіть вас самих при використанні власних класів!) Покладатися на деталі реалізації, яких вони не повинні. (Це не залежить від того, чи ми оголошуємо івари @private
.)
Вставлення змінних екземпляра в процес @interface
компіляції займає більше часу, тому що кожного разу, коли ми додаємо, змінюємо або видаляємо декларацію ivar, ми повинні перекомпілювати кожен .m
файл, який імпортує інтерфейс.
Тому ми не хочемо поміщати змінні екземпляра в @interface
. Куди ми їх повинні покласти?
Варіант 2: у @implementation
фігурних дужках (Не робіть цього)
Далі, давайте обговоримо ваш варіант 2, “Помістіть iVars під @implementantion без блоку фігурних дужок”. Це не оголошує змінні екземпляра! Ви говорите про це:
@implementation Person
int age;
NSString *name;
...
Цей код визначає дві глобальні змінні. Він не оголошує жодних змінних екземпляра.
Добре визначити глобальні змінні у своєму .m
файлі, навіть у вашому @implementation
, якщо вам потрібні глобальні змінні - наприклад, тому, що ви хочете, щоб усі ваші екземпляри мали спільний стан, наприклад, кеш. Але ви не можете використовувати цю опцію для оголошення ivars, оскільки вона не оголошує ivars. (Крім того, загальнодоступні змінні, приватні для вашої реалізації, зазвичай слід оголошувати, static
щоб уникнути забруднення глобального простору імен та ризику помилок під час зв’язку.)
Це залишає ваші варіанти 1 і 3.
Варіант 1: В @implementation
брекетах (Do It)
Зазвичай ми хочемо використати варіант 1: помістіть їх у ваш основний @implementation
блок, у фігурні дужки, наприклад:
@implementation Person {
int age;
NSString *name;
}
Ми помістили їх сюди, тому що вони зберігають їхнє існування приватним, запобігаючи проблемам, про які я розповідав раніше, і тому, що зазвичай немає причин ставити їх у розширення класу.
Тож коли ми хочемо використати ваш варіант 3, помістивши їх у розширення класу?
Варіант 3: У розширенні класу (робіть це лише тоді, коли це необхідно)
Майже ніколи немає причин поміщати їх у розширення класу в тому ж файлі, що і клас @implementation
. Ми могли б просто покласти їх у такий @implementation
випадок.
Але іноді ми можемо написати клас, який є досить великим, щоб ми хотіли розділити його вихідний код на кілька файлів. Ми можемо зробити це за допомогою категорій. Наприклад, якщо ми реалізовували UICollectionView
(досить великий клас), ми могли б вирішити, що хочемо помістити код, який керує чергами багаторазових переглядів (комірок та додаткових подань), в окремий вихідний файл. Ми могли б зробити це, виділивши ці повідомлення в категорію:
@interface UICollectionView : UIScrollView
- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, retain) UICollectionView *collectionViewLayout;
@end
@interface UICollectionView (ReusableViews)
- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;
- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
@end
Добре, тепер ми можемо реалізувати основні UICollectionView
методи в UICollectionView.m
і ми можемо реалізувати методи, які управляють переглядами багаторазового використання UICollectionView+ReusableViews.m
, що робить наш вихідний код трохи більш керованим.
Але для нашого багаторазового коду управління поданнями потрібні деякі змінні екземпляра. Ці змінні повинні бути доступні до основного класу @implementation
в UICollectionView.m
, тому компілятор видасть їх у .o
файл. І нам також потрібно виставити ці змінні екземпляра коду UICollectionView+ReusableViews.m
, щоб ці методи могли використовувати ivars.
Тут нам потрібне розширення класу. Ми можемо помістити багаторазові ivars-view-management у розширення класу у приватному файлі заголовка:
@interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
@end
Ми не будемо передавати цей файл заголовка користувачам нашої бібліотеки. Ми просто імпортуємо його всередину UICollectionView.m
та назад UICollectionView+ReusableViews.m
, щоб усе, що потрібно побачити цим іварам, могло їх побачити. Ми також додали метод, який ми хочемо, щоб основний init
метод викликав, щоб ініціалізувати код управління багаторазовим переглядом. Ми зателефонуємо цьому методу з -[UICollectionView initWithFrame:collectionViewLayout:]
in UICollectionView.m
, а реалізуємо в UICollectionView+ReusableViews.m
.