Можливість розміщувати змінні екземпляра в @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.