3 Core Data для Mac и iOS. Базовая структура кода

Cocoa Touch - версия Cocoa работающая на iOS.

Фактически Cocoa это набор постоянно развивающихся фреймворков: http://developer.apple.com/technologies/mac/cocoa.html

Objective-C 2.0 был анонсирован в 2006 году на Worldwide Developers Conference и был выпущен в Mac OS X v.10.5 (Leopard) к октябрю 2007 года. Основные изменения: automatic garbage collection, синтаксисические улучшения, runtime performance.

Simula 67 первый предшественник Objective-C.

Objective-C построен на основе C с добавлением обмена сообщениями и объектной структуре как в Smalltalk. Со временем были добавлены протоколы, делегаты, категории, блоки.

Objective-C динамический язык, поэтому некоторые вещи которые в C++ делаются на этапах компиляции и сборки, в Objective-C делаются в режиме runtime. Поэтому в случае с Objective-C среда runtime играет гораздо большую роль чем в других языках.


Некоторые базовые типы объявлены в файле Foundation/Foundation.h
Они могу быть реализованы как struct (NSDecimal), или typedef (NSUinteger), или enum
(NSComparisonResult). Иногда такая реализация скрывает фактическую реализацию типа, как в случае с NSInteger, который разрешается в long для 64-битных приложений и int в остальных случаях.

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE ||
TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
#else
typedef int NSInteger;
#endif

Использование этих типов делает код более обслуживаемым и портируемым по сравнению с C.

Префикс NS отсылает нас к NeXTSTEP.

Объекты в Objective-C нужны для того чтобы хранить состояние, также они могут отправлять и получать сообщения. Взаимодействие между объектами проходит посредством сообщений. Данные инкапсулированы в объекты, так что получить состояние объекта можно только посредством отправки ему сообщения.

Устаревший способ объявлять класс:
//
// My_First_ProjectAppDelegate.h // My First Project // // Created by Sams on 6/14/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> @interface My_First_ProjectAppDelegate : NSObject <NSApplicationDelegate> { @private NSWindow *window; } - (NSWindow*) getWindow; - (NSWindow*) setWindow: (NSWindow*)newindow; - (IBAction)saveAction:sender; @end

Символ @ означает директиву компилятора.
Директива @interface может появляться и в других местах, но в основном в .h файлах классов.

По соглашению имена переменных начинающиеся с подчеркивания являются приватными и не должны использоваться напрямую.

Символ * означает ссылку.

Директива @private означает что эта переменная приватная для класса. А директива @protected позволяет наследникам использовать переменную. Директива @public позволяет любым объектам обращаться к переменной напрямую.

Современный способ объявлять свойства класса:
//
// My_First_ProjectAppDelegate.h // My First Project // // Created by Sams on 6/14/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> @interface My_First_ProjectAppDelegate : NSObject <NSApplicationDelegate> { } @property NSWindow* window; - (IBAction)saveAction:sender; @end

Вместо индивидуальных объявлений переменных используются declared properties.

Директива @property работает в паре с директивой @synthesize.
Директива @synthesize создает геттер и сеттер для свойства.

declared properties позволяют использовать dot syntax, который автоматически вызывает соответствующий акцессор.

self всегда ссылается на сам объект, поэтому можно написать:
self.managedObjectContext = <another managedObjectContext>;
или
<myManagedObjectContext > = self.managedObjectContext;

Всё еще остается возможность объявлять переменные: NSWindow *__window;
Причитающаяся директива @property:
@property nonatomic, retain, readonly NSWindow* window;
Связка с @synthesize: @synthesize window = __window;
Доступ к переменной:
__window = <something>;или через dot syntax:
self.window = <something>;

Атрибуты указываются следующим образом:
property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;


Вместо @synthesize можно использовать @dynamic. Это означает что ваш код предоставит всё нужно в режиме runtime. В CoreData используется @dynamic.

Objective-C является messaging средой, а не calling.

C-стиль:
resizeRect (float height, float width){
return height * width;
}

С++-стиль:
myRect.resizeRect (myHeight, myWidth);

Objective-C-стиль:
-(void) resizeRect: (float*) height newWidth:(float*)width {
}
Вызов проходит следующим образом:
[myRect resizeRect: myHeight newWidth: myWidth];

В Objective-C параметры являются именованными. Это значит что эта функция:
-(void) resizeRect: (float*)width newHeight:(float*)height {
не идентична выше указанной.
Технически имена параметров опциональны, но лучше их указывать.

Coding Guidelines for Cocoa

Протоколы

Пример использования протоколов: Multiple Detail Views

Суть решаемой проблемы: control bar вверху окна может быть как navigation bar так и toolbar. Это разные типы компонентов. Кнопка должна показывать в portrait режиме и скрываться в landscape режиме. Решение:
  1. Объявление протокола (набор методов).
  2. Принятие классами протокола.
  3. Реализация методов протокола.
  4. Использование протокола.

1) Объявление протокола в RootViewController.h
@protocol SubstitutableDetailViewController
@required
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
@end

Начиная с Objective-C 2.0 можно помечать методы как обязательные или опциональные. По умолчанию все методы обязательные.

2.1) Принятие протокола:
@interface FirstDetailViewController : UIViewController <
SubstitutableDetailViewController> {
UIToolbar *toolbar;

2.2)
@interface SecondDetailViewController : UIViewController <
SubstitutableDetailViewController> {
UINavigationBar *navigationBar;
}

3.1) Реализация:
#pragma mark -
#pragma mark Managing the popover

- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Add the popover button to the toolbar.
NSMutableArray *itemsArray = [toolbar.items mutableCopy];
[itemsArray insertObject:barButtonItem atIndex:0];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}

- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Remove the popover button from the toolbar.
NSMutableArray *itemsArray = [toolbar.items mutableCopy];
[itemsArray removeObject:barButtonItem];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}

3.2)
#pragma mark -
#pragma mark Managing the popover

- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Add the popover button to the left navigation item.
[navigationBar.topItem setLeftBarButtonItem:barButtonItem animated:NO];
}

- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Remove the popover button.
[navigationBar.topItem setLeftBarButtonItem:nil animated:NO];
}


Нужно провести еще принятие протокола UISplitViewControllerDelegate:
@interface RootViewController : UITableViewController
<UISplitViewControllerDelegate> {
UISplitViewController *splitViewController;
UIPopoverController *popoverController;
UIBarButtonItem *rootPopoverButtonItem;
}

Реализация:
- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc {
// Keep references to the popover controller and the popover button, and tell the
// detail view controller to show the button.
barButtonItem.title = @”Root View Controller”;
self.popoverController = pc;
self.rootPopoverButtonItem = barButtonItem;
UIViewController <SubstitutableDetailViewController> *detailViewController =
[splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:rootPopoverButtonItem];
}

- (void)splitViewController:(UISplitViewController*)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
// Nil out references to the popover controller and the popover button, and tell
// the detail view controller to hide the button.
UIViewController <SubstitutableDetailViewController> *detailViewController =
[splitViewController.viewControllers objectAtIndex:1];
[detailViewController invalidateRootPopoverButtonItem:rootPopover
ButtonItem];
self.popoverController = nil;
self.rootPopoverButtonItem = nil;
}

Локальная переменная *detailViewController объявляется с типом UIViewController и принимающей протокол SubstitutableDetailViewController. Т.к. она принимает протокол то можно быть уверенным в том что реализуются методы протокола.

Категории чем-то похожи на протоколы. Тоже содержать только методы. Используются для модификации класса без доступа к его коду. В runtime режиме нет разницы между родными методами класса и методами добавленными через категорию.

Делегаты

Часто функциональность оборачивается в протоколы. И некоторые из этих протоколов спроектированы для использования делегатами.

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

В предыдущем параграфе класс RootViewController принимал протокол UISplitViewControllerDelegate. Это значит что RootViewController можно назвать делегатом объекта который требует реализацию этого протокола.

Импорт

Директива #import лучше #include тем что проверяет был ли уже импортирован файл.
Файлы из проекта заключены в кавычки, а файлы из фреймворков в угловые скобки.

В interface файле часто приходится объявлять протоколы и классы, которые будут определены позже на этапе сборки. В таком случае можно вместо #import "myclass.h" использовать @class myclass

Резюме

Отличие от Objective-C от других языков:
  • messaging syntax вместо function calling syntax;
  • расширение кода посредством protocols, delegates, categories вместо subclassing.