Показаны сообщения с ярлыком Cocoa. Показать все сообщения
Показаны сообщения с ярлыком Cocoa. Показать все сообщения

четверг, 7 мая 2009 г.

7.05.09. Application Architecture.Document Architecture

Document Architecture

Особенности document-based application:

  • возможность создания нового документа,
  • сохранение этого документа,
  • загрузка документа позже из файла,
  • каждый документ - в отдельном окне,
  • централизованное управление и мониторинг открытыъ документов в окнах.

Ключевые классы: NSDocument, NSDocumentController, and NSWindowController.

Объект NSDocumentController управляет объектами NSDocument. Каждый NSDocument управляет одним или несколькими объектами NSWindowController. В каждом NSWindowController происходит сопоставление документа с одним или несколькими окнами. NSWindowController управляет представлением документа.

Разделение обязанностей:

NSDocumentController управляет документами (созданием, сохранением, загрузкой). Это координирующий контроллер. Информацию о документах он берет из списка свойств приложения Info.plist. Готов к употреблению (подклассов создавать не нужно).

NSDocument является моделью документа. Это модель-контроллер. Абстрактный класс, нужно создать производный класс конкретно для своей модели документа. Должен уметь загружать/сохранять объекты модели. Возможно будет иметь несколько способов хранения данных. Управляет одним или несколькими контроллерами NSWindowController.

NSWindowController управляет представлением документа в окне. Это представление-контроллер. В простых случаях не требуется создания подкласса, этот контроллер обладает минимумом, необходимым для управления окном. Однако, в более сложных случаях понадобится создавать подклассы, обладащие информацией о представлениях документов. Обычно, экземпляр этого подкласса является File's Owner для nib-файла документа. Если документу нужно несколько окон для представления, то для каждого из них нужен свой NSWindowController.

Подробнее.

6.05.09. Application Architecture.Nib Files

Nib Files and Other Application Resources

В nib-архиве сохраняется весь граф объектов, при этом каждый объект остается уникальным и все отношения сохраняются.

При архивировании каждому объекту отправляется команда закодировать себя в поток. При разархивировании происходит обратная операция, причем каждый объект декодирует себя сам.

Процесс кодирования может быть последовательным (протокол NSCoder) и основанным на использовании ключей (используются классы NSKeyedArchiver and NSKeyedUnarchiver). Также должен быть реализован протокол NSCoding.

В nib-файле сохраняется весь граф объектов интерфейса со всеми отношениями между объектами. В нем должен быть корневые объекты для иерархии представлений, объекты контроллеров, прокси-объекты для доступа к нужным классам. Кроме того, в nib-файле всегда должен быть:

  • File's Owner. Объект, который владеет nib-файлом и управляет объектами в нем. Этот владелец должен быть внешним по отношению к nib-файлу. Он является канал для связи внутренних и внешних объектов.
  • First Responder. Первый объект в цепочке responder chain. Его также можно назначить target-ом.

Для класса представления, производного от NSView, нужно использовать объект Custom View из палитры. При разархивировании будет вызван метод awakeFromNib.

В приложении есть главный nib-файл, у которого File's Owner - NSApp. Он содержит основное меню и возможно некоторые окна. При запуске программы главный nib-файл разархивируется и окна раскрываются. Объекты из других nib-файлов загружаются при необходимости.

Ресурсы приложения хранятся также в nib-файле в папке Resources. При выполнении программы доступ к ресурсам возможен через объект класса NSBundle.

Приложение может динамически загружать дополнительные bundles, в которых могут находиться как ресурсы, так и исполняемый код. Этим способом легко организовать поддержку плагинов.

6.05.09. Application Architecture.Controls and Menus

Controls and Menus

Также см. один из прошлых постов.

Контролы являются производными от NSControl, который порожден от NSView.

Ячейки же являются производными от NSObject и их классы соответствуют контролам: NSButtonCell, NSStepperCell, NSTextFieldCell.

Controls That Manage Multiple Cells

Если у контрола несколько ячеек, то он может использовать один экземпляр cell для отображения как шаблон, меняя только содержимое при отображении. Так делают NSTableView and NSOutlineView.

NSMatrix использует для каждого элемента отдельный экземпляр cell. Каждая ячейка создается как копия прототипа.

How Controls Manage Cells

Контрол - это полноценное представление, поэтому он может сам реализовать методы NSResponder и сам стать обработчиком событий и участником цепочки responder chain, также самостоятельно обрабатывать сообщение drawRect:. Однако, вместо этого контрол передает эти обязанности своей ячейке.

Контрол в обработчике drawRect: отправляет сообщение drawWithFrame:inView: ячейке, передавая ей прямоугольник, в котором находится контрол. При этом фокус отрисовки фиксируется на контроле, поэтому ячейка может сразу же приступат к отрисовке.



Ячейки при отрисовке должны выполнить обязанности, возложенные на них их классом. Это может быть форма, цвет, стиль, специальное состояние (серый текст для неактивной ячейки). Кроме того, ячейка должна отобразить своё содержимое, которое обычно является строкой или преобразовывается к строке. С ячейкой также ассоциируются вспомогательные объекты такие, как NSColor.

Индивидуальности ячейке добавляют действия (action), которые конкретная ячейка может отправлять target-у. Действия - это селекторы вызываемых методов у цели. Этот аспект задается с помощью NSActionCell.

Rationale for the Control-Cell Architecture

Зачем нужны ячейки? Почему всю работу не может выполнить сам контрол?

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

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

Удобно использовать NSTableView с прототипами ячеек, которые используются при тиражировании элементов таблицы.

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

Menu Characteristics and Architecture

Меню и элементы меню: NSMenu and NSMenuItem.

NSMenuItem содержит селектор для действия и указатель на target.

Перед тем, как показать меню, NSMenu проверяет responder chain для каждого пункта на предмет возможности обработки сообщения от этого пункта. Если ничего подходящего не найдено, то пункт меню блокируется.

Represented Objects

Ячейка и пункт меню могут иметь ассоциированные с ними представленные объекты (Represented Objects). Target может запросить отправителя сообщения-действия о таких объектах.

- (void)changeColor:(id)sender {
NSColor *repObj = [sender representedObject];
[textView setBackgroundColor:repObj]; // textView is an outlet
}

Представленный объект может быть объектом класса NSView и вообще объектом любого класса.

среда, 6 мая 2009 г.

6.05.09. Application Architecture. Views, Responders.

Views

Представление основной объект для отображения информации и приема событий от клавиатуры и мыши. Реальные представления являются подклассами NSView.

Представления можно разделить на:

  • Контролы (кпопки, текстовые поля и т.п.), с которыми пользователь может совершать действия. Обычно идут вместе с ячейками, которые не являются производными от NSView.
  • Контейнеры. Содержат (более удобно оформляют) более примитивные представления.  NSTextView, NSImageView, NSBox, NSSplitView, and NSTabView.
  • Составные представления. Объединяют несколько представлений. 
  • Оболочки. Дают доступ к некоторым системным возможностям  (NSOpenGLView and NSMovieView).

The View Hierarchy

Представления объединены в иерархию. У каждого есть родитель (superview). Базовый родитель - Content View. У него есть только закрытый superview.

Система координат к каждого представления своя.

Каждое предсталение знает о

  • окне
  • superview
  • дочерних представлениях


View Geometry and Coordinates

The Frame

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


Представления могут быть перемещены и повернуты относительно начала координат родительского представления. 



The Bounds

В то время как frame задает размеры представления и его положение относительно родительского представления, bounds определяют область представления, в котором происходит отрисовка содержимого и обработка событий. По умолчанию frame и bounds совпадают. Т.о. совпадают и начала координат в левом нижнем углу. Однако, начало координат локальной системы координат представления можно перенести в левый верхний угол, что удобно при отображении текста сверху-вниз слева-направо. В этом случае получим flipped coordinates.


Возможно преобразование системы координат представления:

  • начало координат bounds может быть перенесено из точки начала координат frame
  • для bounds может быть свой масштаб по осям
  • система коорнинат bounds может быть повернута относительно системы координат frame.

How Views Get Drawn

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

Для немедленного обновления представлении вызываются display методы. При этом 

  • блокируется представление, требующее обновления
  • вызывается метод представления drawRect: 
  • обновляется буфер окна

Хотя рекомендуется использовать auto-display mechanism. В этом случае все представления будут обновлены совместно с учетом их взаимного расположения.

Для обновления отдельного представления (целиком и по частям) существуют методы:

display, displayRect:, displayRectIgnoringOpacity:.

Для указания необходимости обновления при использовании auto-display mechanism:

setNeedsDisplay:, setNeedsDisplayInRect:

Т.е. этими методами представления помечаются для обновления.

Можно немедленно обновить помеченные представления:

displayIfNeeded, displayIfNeededInRect:, displayIfNeededIgnoringOpacity, эdisplayIfNeededInRectIgnoringOpacity:.

Этот способ промежуточный (в т.ч. по эффективности) между немедленным обновлением одного представления и автоматическим обновлением всех представлений.

Locking Focus

Для отрисовки на представление фиксируется фокус (lockFocus..., unlockFocus). При этом происходит:

  • система координат представления становится текущей для всего приложения
  • вычисляется прямоугольная область, за пределами которой отрисовка невозможна
  • настривается среда для отрисовки

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

В состоянии находится:

матрица преобразований координат точек из одной системы координат в другую, а также ряд параметров

The current color for fill and stroke operations
Alpha value (transparency)
Line attributes, including width, join, cap, dash, and miter limit
Anti-aliasing value
Current color space
Text attributes: font, font size, character spacing
Blend mode

Может быть несколько представлений с зафиксированными фокусами. Это происходит в случае отрисовки группы связанных представлений. Фокусировка начинается с superview и идет далее к дочерним представлениям. При переходе к фокусировке на следующем представлении предыдущий фокус помещается в состояние, а состояние помещается в стек.



Текущим становится очередное состояние из стека.

What Happens in drawRect:

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

Основным правилом оптимизации отрисовки является отрисовка только нужных в данный момент областей как можно меньшего размера. Обращение к оконному серверу, координирующему отрисовку - дорогая операция и ее нужно выполнять только по необходимости. Поэтому отрисовка всего представления - наименее эффективный вариант. Лучше запросить у NSView список областей, требующих обновления с помощью  getRectsBeingDrawn:count:

Методы, используемые в drawRect:

Drawing functions (declared in NSGraphics.h) to draw, fill, erase, and perform other operations with rectangles 
Methods to construct lines and shapes with bezier paths (NSBezierPath class)
Methods to create and apply affine transforms, involving translation, scaling, and rotation operations (NSAffineTransform class)
Color and color-space methods (NSColor and NSColorSpace)
Methods for creating and compositing images (NSImage and various image-representation classes)
Methods for drawing text (NSString and NSAttributedString)

Эти методы напрямую обращаются к оконному серверу за реализациями из Core Graphics (Quartz). К реализациям можно обратиться и напрямую.

Threads and Drawing

Отрисовка необязательно может идти только из главной нити, однако изменения свойств NSView, например, прямоугольника для Frame, должно происходить только из главной нити. Кроме того, только одна нить может рисовать в данном представлении в данный момент времени.

Views and Events

Представления в первую очередь обрабатывают события от пользователя. Для этого представление реализует протокол NSResponder, в котором объявлены такие методы, как  mouseDown:, mouseMoved:, and keyUp:. В эти методы как параметр передается объект NSEvent. Если представление не содержит подходящего обработчика, то он ищется у superview и далее по responder chain.

Responders and the Responder Chain

NSResponder определяет поведение NSApplication, NSWindow, and NSView с точки зрения обработки событий. 

Сообщения о событиях делятся на 

  • события (event messages),
  • действия (action messages), которые перенаправляются target-ам.

Также NSResponder задает механизм для управления responder chain, т.е. передачей событий по цепочке обработчиков.



У каждого окна есть своя цепочка responder chain. Основные объекты в ней - это NSWindow и семейство представлений. Чем ниже представление в иерархии, тем больше шансов, что именно оно обработает событие. У окна NSWindow есть первый обработчик (first responder), обычно выбранное представление в окне.

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

Для действий длина цепочки определяется следующими правилами:

  • если в приложении есть главное окно и ключевое окно, то сначала действие пытается обработать цепочка ключевого окна, класс ключевого окна, его делегат, затем цепочка главного окна, класс главного окна, его делегат и наконец NSApp и его делегат.
  • для каждого типа приложения simple, document-based, или приложение с window controller структура цепочки отличается.

понедельник, 4 мая 2009 г.

4.05.09. Application Architecture.

The Global Application Object

NSApp - глобальный объект NSApplication.

The Main Event Loop

В функции main вызывается функция NSApplicationMain, в которой происходит следующее:

  1. Приложение получает объект NSApp с помощью метода sharedApplication класса.
  2. Загружается главный nib-файл.
  3. Запускается основной метод приложения ([NSApp run]).

В п.1. также устанавливается связь с оконным сервером, от которого будут поступать системные события.



В п.3. работает бесконечный цикл.

  • В нем работают наблюдатели, получающие события и обрабатывающие их.
  • Очередное событие извлекается из очереди событий (nextEventMatchingMask:untilDate:inMode:dequeue:).
  • Обработка события заключается в его перенаправлении нужного объекту (как правило, NSWindow object (sendEvent:)) или отрисовке преставления.

Как только очередь исчезает, то оконный сервер снова начинает ее заполнять.

More About Event Dispatch

События могут быть весьма разнообразными. Поэтому и получатели событий выбираются NSApp разные (NSApp работает диспетчером).

Например, событие нажатия клавиши перенаправляется ключевому окну (Key Window) с помощью sendEvent:

Событие клика мышью отправляется окну, в котором находился курсор.

Событие клика мышью на контроле порождает сообщение-действие и отправляется с помощью sendAction:to:from: указанной цели или перенаправляется по цепочке (responder chains).

Также возможны другие типы событий.

Window Management

NSApp содержит список окон, с помощью которого управляет ими.

Handling Apple Events

Существуют несколько системных событий (начинаются на kAE), отправляемых приложению при запуске или действиях в Finder-е и т.п.

Windows

По способу работы с окнами приложения могут быть:

  • Document-based
  • Single-window

Также могут модальные окна в виде панелей.

Окна NSWindow создает оконный сервер. Работа с окнами идет по их номеру. Сервер после создания окна получает графический контекст окна и window's backing store - область памяти для работы с устройством отображения окна.

Window Buffering

Если у окна есть буфер, то сервер может показать невидимую часть окна, не обращаясь к приложению. Иначе, приложению придется перерисовывать эту часть.

Возможны несколько типов буферизации.

Window Z-Order and Levels

Окна объединяются по уровням согласно их назначению. Некоторые уровни всегда располагаются выше других. Уровнями и z-порядком управляет оконный сервер, а также NSWindow для окон приложения.

Если окно убрано из списка видимых окон, то сообщения ему не доставляются.

Parts of a Window

У окна есть два представления: frame view and a content view. Они являются подклассами NSView. Frame включает заголовок, границу и служебные кнопки. Этот объект представления создается NSWindow как private и его нельзя изменить, создав подкласс. Однако при создании этой переменной можно указать, какие кнопки показывать и т.д.

Content view имеет superview, но оно является также private объектами. Поэтому Content view является корневым представлением для всех представлений, доступных для изменения. В итоге образуется иерархия представлений. Представление имеет ссылку на своё окно.



Window Coordinates

Для расположения окон на экране используется система координат экрана.

У окон своя система координат.

Начало координат в левом нижнем углу.

Frame view использует систему координат экрана. Content view - окна.

Windows and Drawing

Отрисовка - обязанность представлений. Однако NSWindow следит за областями представлений, требующими обновлений и дает им команду на обновление (display, displayRect:, or displayRectIgnoringOpacity:).

Window Status

Окна (главное и ключевое) могут иметь разный статус (активное, неактивное и т.п.).

Event Dispatch

Большинство событий приходит через sendEvent: к NSWindow. Дальше оно перенаправляет событие нужному представлению.

Panels

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

Подробнее.

воскресенье, 3 мая 2009 г.

3.5.09. Bindings, Notifications

Bindings

Привязка какого-либо значения из модели к элементу представления. При изменении значения на любой из сторон отношения сразу же меняется значение на другой стороне.

Привязка идет через контроллер.

С помощью привязок сеть отношений между объектами модели можно расширить на контроллеры и далее на элементы представления.

Для работы с привязкой нужно, чтобы классы поддерживали key-value coding and key-value observing.

Подробнее.

Notifications

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

Широковещательная рассылка уведомлений - способ уменьшить связанность подсистем. Наблюдателей может быть несколько, а отправитель может о них не знать.



Вводится центральный элемент - диспетчер событий.

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

Подробнее.

Сравнение уведомления с делегатами.

Преимущества уведомлений (отношения один-ко-многим):

  • отправитель не знает получателей, которых может быть много
  • уведомления могут быть разного типа
  • уведомления могут быть межпроцессные

Преимущества делегатов (отношение один-к-одному):

  • делегат может вернуть значение отправителю и повлиять на само событие (например, отменить его)

Преимущества привязок по сравнению с уведомлениями:

  • Привязки также можно рассматривать как аналоги уведомлений, причем это более дешевый способ, т.к. не тратятся ресурсы на переодические отправки уведомлений и содержание посредника.

Преимущества уведомлений по сравнению с привязками:

  • уведомления могут сообщать об изменении не только свойств,
  • может быть неудобно делать классы KVO, KVB совместимыми.

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

Рекомендации по использованию уведомлений:

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

The Notification Object

NSNotification. Содержит имя (тег) уведомления, объект, который может быть любым объектом, который отправитель хочет отправить наблюдателю. Обычно - это сам объект отправителя (аналог sender). Также содержит словарь для передачи вспомогательной информации.

Notification Centers

NSNotificationCenter. Диспетчер уведомлений от отправителей к наблюдателям.

Notification Queues



Содержит очередь уведомлений. 

Очередь может очищаться от одинаковых уведомлений. Для определения одинаковости вводятся несколько критериев (по имени, по отправителю и т.д.). 

Уведомление может иметь индикатор для перемещения из очереди в центр уведомлений. Сам центр может находиться также в нескольких состояниях. Перемещение уведомления из очереди в центр происходит при совпадении индикатора уведомления и состояния центра.

Способы освобождения очереди (перемещения уведомлений в центр)

NSPostASAP - Posting As Soon As Possible. Уведомления в центре обслуживаются в цикле. При этом способе уведомление перещается в центр сразу же после окончания очередной итерации.

Пример: запросы на отрисовку составного изображения на поверхности окна. Запросы поступают из многих источников. Окончательное изображение формируется на сервере. Было бы накладно отправлять поверхность на сервер после обслуживания каждой операции. Поэтому все отправители, рисующий на поверхности, ставят в очередь уведомление FlushTheServer. Дубликаты этих уведомлений удаляются. Это уведомление имеет атрибут NSPostASAP. Как только текущий цикл обработки запросов на рисование будет выполнен, одно уведомление FlushTheServer будет передано в центр и выполнено, т.е. поверхность будет передана на сервер и отображена в окне.

NSPostWhenIdle - Posting When Idle. Уведомление уходит в центр, если он находится в состоянии ожидания. Время жизни входов центра может закончиться и он перестанет находиться в состоянии ожидания. В этом случае уведомление не будет отправлено.

NSPostNow - Posting Immediately. Синхронный способ передачи. Центр обслужит уведомление и сообщит, что наблюдатели его обработали.

Ownership of Delegates, Observers, and Targets

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

Если создается класс, производный от класса фрейворка, в котором есть делегат, источник данных, наблюдатель или цель, то производный класс не должен обладать этими объектами.

Поэтому при архивировании сохранять все эти объекты нужно явно.

пятница, 1 мая 2009 г.

1.05.09. The Target-Action Mechanism.

Механизм, связывающий событие в контроле GUI и инструкцию по его обработке в некотором другом объекте.

Событие гененерирует cell, в котором размещен контрол. Ловит событие (т.е. сообщение о нем) target, его обработчик события - это action.

Cell обладает информацией о том, как сгенерировать и послать событие. Также она содержит в виде outlet-а информацию о target-е.

Этот outlet может иметь значение nil. В этом случае обработчик события может быть назначен или определен в процессе работы программ. В качестве обработчика подойдет любой объект, у коротого есть реализация нужного метода. Поиск объекта ведет NSApplication.

Определение объекта-обработчика идет по следующей последовательности:

  1. Проверяется, т.е. ищется подходящий метод, у first responder-а в key window (окно, которое реагирует на нажатия клавиш и сообщения от меню и диалогов). От first responder с помощью nextResponder перемещаемся по цепочке responder-ов до класса окна NSWindow.
  2. Проверяется NSWindow и его делегат.
  3. Если главное окно не совпадает с key window, то проверяется цепочка от first responder до NSWindow и делегата главного окна.
  4. Последним объектом для поиска является объект приложения и его делегат.

Контролы не являются владельцами target-ов также, как хост-классы не являются владельцами делегатов и источников данных. Их владельцами дожны быть клиенты контролов и хост-классов.

Action - это и сообщение, отправляемое контролом target-у и метод-обработчик target-а.

Cell содержит действия в виде переменных типа SEL (указатель на функцию). 

Сигнатура сообщения для действия в cell:

- (IBAction) deleteRecord:(id)sender;

IBAction - это void для IB.

Для того, чтобы разгрузить контрол в ячейки перенесена некоторая работа из контрола, например, вся работа по реализации механизма target-action. 



NSMatrix может содержать несколько ячеек. 

NSCell  - абстрактный класс. NSActionCell - конкретный класс, содержащий переменные для target и action.

Ячейка также обычно ответственна за отображение контрола, например, она подсвечивает его, когда над ним проводится курсор.

Иногда, как например у  NSMenuItem, ячейка не используется и необходимые переменные содержатся сразу в контроле.

Для программной установки target и action:

- (void)setTarget:(id)anObject;
- (void)setAction:(SEL)aSelector;

Примеры:

[aCell setTarget:myController];
[aControl setAction:@selector(deleteRecord:)];
[aMenuItem setAction:@selector(showGuides:)];

четверг, 30 апреля 2009 г.

30.04.09. T&T. Узнать, в каком классе упало приложение

Если нужно узнать, в каком классе произошло обращение к освобожденному указателю, то

Executables - Get Info - Arguments - Add Variable - NSZombieEnabled=YES

и при выполнении программы, когда появляется иконка GDB, то можно перейти в окно консоли, нажать Continue и увидеть, где возникла ошибка.

Также для обнаружения утечек памяти служит инструмент Leaks. (Run - Start with Performance Tool - Leaks).

30.04.09. Outlets, Delegates.

Outlets

Outlet - переменная экземпляра, с помощью которой устанавливается связь с другим объектом. Эти связи архивируются в nib-файл и восстанавливаются при загрузке nib-файла.

Обычно таким способом связываются объекты-элементы графического интерфейса и необходимые контроллеры, но могут быть связаны и любые объекты, чьи экземпляры представлены в IB.

Delegates and Data Sources

Делегирующий объект обычно наследует от NSResponder, т.е. становится способным обрабатывать событие от пользователя.

Делегирующий класс имеет у себя свойство или outlet с именем delegate.

Также он должен объявить без реализации несколько методов, которые будет реализовывать делегат. Эти методы должны быть объединены в формальный или неформальный (категория NSObject) протокол. Из формального протокола делегат будет обязан реализовать все методы, кроме отмеченных как optional. Из неформального протокола он может реализовывать любые методы.


Имя метода для делегата состоит из типа возвращаемого значения, имени делегирующего объекта с маленькой буквы без NS, аргументов.


- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename; // NSApplication
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url; // UIApplicationDelegate
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; // NSApplication
- (UITableRowIndexSet *)tableView:(NSTableView *)tableView willSelectRows:(UITableRowIndexSet *)selection; // UITableViewDelegate
- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)newFrame; // NSWindow

Имя обычно включает глаголы: “Should” or “Will” для событий, которые только должны произойти и “Did” or “Has”, которые уже произошли.

Если событие должно произойти, то делегат может на него повлиять, например, вернуть No для сообщения applicationShouldTerminate.

Для произошедших событий сообщения делегатам носят информирующий характер.


- (void) tableView:(NSTableView*)tableView mouseDownInHeaderOfTableColumn:(NSTableColumn *)tableColumn; // NSTableView
- (void)windowDidMove:(NSNotification *)notification; // NSWindow
- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame; // UIApplication
- (void)applicationWillBecomeActive:(NSNotification *)notification; // NSApplication

Если имя сообщения содержит Will, но при этом тип возвращаемого значения void, то это значит, что делегат не может повлиять на факт возникновения события, но может подготовить программу к его появлению.

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

Сделать свой объект делегатом можно в IB, соединив outlet`а delegate с экземпляром своего объекта.

Делегирующие объекты не должны увеличивать счетчик ссылок на делегата, т.е. посылать ему retain. Однако клиенты делегирующего объекта должны проверить в каком состоянии находится делегат и послать ему retain.

Пример: пусть у приложения есть контроллер и у окон этого приложения есть контроллер и они являются делегатами соответственно приложения и окна. Тогда найти контроллер окна можно так:

id winController = [[NSApp keyWindow] delegate];

А контроллер приложения так:

id appController = [NSApp delegate];

среда, 29 апреля 2009 г.

29.04.09. Design Patterns. Memento, Наблюдатель, Прокси, Одиночка, Шаблонный метод

Memento

Сохраняет внутреннее состояние объекта, не нарушая инкапсуляции так, чтобы можно было позже восстановить его.

Применяется при архивировании объектов. Объекты при этом должны реализовывать протокол NSCoding. Для архивирования используется объект NSCoder и классы NSKeyedArchiver and NSKeyedUnarchiver.

В простых случаях возможно также сохранение объектов классов NSDictionary, NSArray, NSString, NSData, NSDate, and NSNumber в виде списка свойств. Этот способ работает только для графа объектов, состоящего из указанных классов.

Кроме того, для сложных графов объектов можно применять фреймворк Core Data.

Observer

Зависимость один-ко-многим.

Notifications

Наблюдаемый объект посылает уведомление в центр уведомлений, который находит зарегистрированных наблюдателей для этого уведомления и отправляет им специальное сообщение, в параметрах которого указывается наблюдаемый объект и словарь с необходимой информацией. Уведомление характеризуется именем.

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

Можно использовать для того, чтобы проверить, что все объекты документа сохранили своё состояние перед тем, как окно документа закроется.

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

Key-Value Observing

Уведомление наблюдателей об изменениях в свойствах наблюдаемого объекта. Умедомление происходит напрямую без центрального объекта.

В MVC таким способом представление уведомляется об изменениях в модели.

Для работы с этим типом уведомлений объекты должны быть KVO-совместимыми.

Proxy

Прокси похож на декоторар. Прокси контролирует доступ к субъекту, декоратор добавляет к нему поведение (и возможно состояние).

Для создания прокси можно использовать абстрактный класс NSProxy, который реализует протокол NSObject и поэтому его можно использовать как корневой объект.

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

Singleton

Единственный объект, имеющий доступ к ценному ресурсу. Создается при первом обращении.

Примеры:

NSFileManager, NSWorkspace, NSApplication

Template Method

Задает структуру алгоритма. Некоторые шаги могут быть переопределены в подклассах.

Document Architecture построена с использованием этого принципа, т.е. обычно нужно создать производные классы и в них переопределить некоторые методы, чтобы другие методы воспользовались переопределенным методом.

29.04.09. Design Patterns. Категории, фасад, итератор, медиатор

Categories

Категории являются еще одним способом расширить класс (только добавить поведение) без создания подкласса.

Этим способом можно также сгруппировать близкие по смыслу методы.

Категории добавляются к классу на этапе компиляции.

Facade

Предоставляет удобный доступ к подсистемам.

Пример: NSImage, который содержит несколько вариантов представлений изображений, которые автоматически выбираются в зависимости от разрешения и т.п.

Iterator

Перекладывает ответственность по работе с элементами коллекций с самих коллекций на специальные объекты-итераторы. Структура коллекции скрывается от клиента.

Производные от NSEnumerator классы задают поведение итератора для различных коллекций.

NSArray, NSSet, and NSDictionary имеют метод, возвращающий подходящий итератор. Работа с любым итератором задается методами NSEnumerator. Например, для получения очередного элемента нужно отправить сообщение nextObject.

Mediator

Централизует взаимодействие объектов. Объекты начинают общаться не напрямую, а через посредника. Это позволяет менять способ взаимодействия объектов и посредника так, что изменения, касающиеся одного объекта, не затрагивают других.

Контроллеры являются посредниками в MVC.

Контроллеры-медиаторы (производные от NSController) перенаправляют запросы от представления к модели. Поддерживают технологию привязок (bindings) элементов интерфейса к объектам модели. Экземпляры таких контроллеров доступны через IB и поэтому доступны для повторного использования.

NSObjectController - для управления одиночным объектом из модели.

NSArrayController - для управления набором объектов модели. Управляет выбором, добавлением и удалением.

NSTreeController - для управления иерархической структурой объектов модели.

NSUserDefaultsController - для управления настройками.

Контроллеры-координаторы централизуют взаимодействие между объектами. Обычно производны от NSWindowController или NSObject. Являются делегатами классов из фреймворка и целями (targets) для сообщений-действий. Из-за реализации под конкретную программу не взаимозаменяемы.

Без контроллеров-медиаторов связать можно любую пару объектов, если они реализуют неформальные протоколы NSKeyValueCoding and NSKeyValueObserving. Но лучше использовать явного посредника, производного от NSController.

Задачи координирующих контроллеров, решаемые через IB:

  • управляют outlets между объектами модели и объектами представления,
  • являются целями в схеме target-action, сообщения-действия инициируются элементами пользовательского интерфейса,
  • являются делегатами для классов фреймворка.

Все эти связи сохранются в nib-файл.

вторник, 28 апреля 2009 г.

28.04.09. Design Patterns. Абс. фабрика, адаптер, цепочка, команда, target-action, композит, делегат

How Cocoa Adapts Design Patterns

Два основных паттерна в Cocoa: “The Model-View-Controller Design Pattern” and “Object Modeling.”

Abstract Factory

Разновидность фабрики реализуется через Class Cluster.

Adapter

Под адаптером понимается способ добавить классу протокол, так чтобы другой объект, умеющий работать с этим протоколом, а не с любым интерфейсом любого класса, мог работать с этим классом.

Интерфейс - как способ  скрыть конкретный класс.

Chain of Responsibility

Огранизует цепочку объектов, которые могут по очереди взять или не взять на себя обработку сообщения. Если объект не берет обработку сообщения, то он передает его дальше по цепочке.

Обычно такая цепочка получается в случае композиции элементов окон и представлений. Поиск обработчика начинается с самого нижнего объекта в иерархии и в конце концов может дойти до корнего объекта Window. Также сообщение может обработат делегат окна.

Цепочек может быть несколько, но только одна из них может быть активной.

Для организации объектов цепочки нужно использовать базовый класс NSResponder.

Command

Содержит запрос к объекту. Может включать несколько действий, которые должен будет выполнить получатель сообщения с этой командой.

NSInvocation содержит сообщение, получателя сообщения и аргументы. Меняя получателя сообщения, можно заставлять выполнять одну и ту же команду разные объекты.

The Target-Action Mechanism

Элемент управления (кнопка и т.п.) может послать сообщение целевому объекту (обычно контроллеру), в результате которого будет выполнено некоторое действие. Действие определяется селектором. Объект Cell элемента управления объединяет целевой объект и действие, т.е. обработчик сообщения. Элемент управления размещен в ячейке.

Composite

Используется в MVC при построении View. Корневым элементом является NSWindow. В нем находится Content View, прозрачная область покрывающая всю внутренную часть окна.



В итоге все View образуют иерархию представлений. Сообщение, отправленное представлению, также доходит и до его дочерних представлений.

Представление имеет две рамки: внешнюю frame и внутренню bounds.

Frame определяет положение представления относительно родительского представления.

Bounds задает координаты внутренней области представления.

Decorator

Воплощает принцип: класс должен быть открыт для расширения, но закрыт для изменения.

Delegation

Первый способ расширить класс (добавить поведение) без создания подкласса.

Хост-объект содержит ссылку на объект-делегат. Сам хост-объект умеет выполнять некоторую операцию обобщенно, что приводит к тому, что реальные действия по этой операции выполняет объект-делегат.

Хост-объект - обычно класс фреймворка. Делегирование - способ конкретизировать поведение такого класса без создания подкласса.

Делегат в Cocoa должен реализовывать методы формального или неформального протокола хост-класса. Хост-класс, прежде, чем отправить сообщение делегату, проверяет наличие метода с помощью respondsToSelector:.

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

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

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

пятница, 24 апреля 2009 г.

24.04.09. Design Patterns. Command

Cocoa Design Patterns

Решение задачи, находящейся в контексте.

Патерном описывается обобщенное решение задачи, достигающее некоторой цели при наличии ограничений. Задача рассматривается для определенного контекста.

Конкретная структура решения - "экземпляр" паттерна.

Инкапсулировать изменяющиеся части системы. Проектировать в терминах интерфейсов, а не реализаций.

An Example: The Command Pattern


В Cocoa реализован в NSInvocation для промежуточного хранения отправленных сообщений (undo, состояния).


24.04.09. Ошибки, ресурсы

Error Handling

Исключения в Cocoa предназначены для незапланированных ошибок.

Для запланированных исключительных ситуаций (не открыт файл и т.п.) используется nil, NO, NULL. Чтобы сообщить дополнительную информацию об ошибке используется NSError.

Чтобы сообщить пользователю об ошибке можно использовать NSAlert.

Resource Management and Other Efficiencies

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

Для каждого copy и retain должен быть свой release.

Functions, Constants, and Other C Types

Для повышения производительности можно пользоваться средствами Си: функциями, структурами, enum-ами.

четверг, 23 апреля 2009 г.

23.04.09. The Foundation, Application Kit class hierarchy


Zooming in on the Cocoa architecture—some major dependencies



The Foundation class hierarchy






Overview of the Application Kit




23.04.09. Обязательные методы для подкласса.

Object Infrastructure

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

  • isEqual: and hash
  • description
  • copyWithZone:
  • initWithCoder: and encodeWithCoder:

23.04.09. Key-Value.

Key-Value Mechanisms

Существуют Key-value binding, key-value coding, and key-value observing.

  • KVB управляет привязками объектов друг к другу.
  • KVC c помощью реализации неформального протокола NSKeyValueCoding позволяет получить значение свойства по его имени через специальный метод этого протокола.
  • KVO c помощью реализации неформального протокола NSKeyValueObserving позволяет объектам регистрироваться в качестве наблюдателей за другим объектом. Наблюдаемый объект автоматически уведомит наблюдателей при изменении своих свойств.

Для того, чтобы сделать свойства объекта совместимыми с этой техникой:

  • в случае атрибутов или отношений 1-к-1 нужно для setter-а заводить метод setKey, а для getter-а - Key,
  • в случае свойства как отношения 1-ко-многим, т.е. коллекций в объекте, нужно getter-у давать имя свойства и если getter не должен возвращать изменяемую коллекцию, то нужно реализовать insertObject:inKeyAtIndex: and removeObjectFromKeyAtIndex: из NSKeyValueCoding.

Для автоматического KVO объект должен удовлетворять KVC.

23.04.09. Методы жизненного цикла. Свойства, getter-ы и setter-ы.

Basic Subclass Design

Объявление класса:



@interface Controller : NSObject {
// атрибуты
}
// методы
@end

При наследовании от некоторого полезного класса фреймворка придется переопределять некоторые методы. После этого нужно сделать так, чтобы эти методы были вызваны фреймворком вместе методов базового класса. В Interface Builder обычно для этого указывается порожденный класс вместе базового.

Instance Variables

Переменная с IBOutlet показывает, что внешние связи будут восстановлены автоматически при загрузке nib-файла.

Entry and Exit Points

Cocoa посылает объектам и классам, которые тоже являются объетком, в разные моменты жизни объекта определенные сообщения. 

  1. initialize. Сообщение получает класс. Это самое первое сообщение, получаемое классом до того, как сообщения получат экземпляры. Сначала сообщение получает суперклассы, затем подклассы. Обычно используется для инициализации некоторого общего состояния, используемого всеми экземплярами.
  2. init. Инициализация экземпляра. Может быть будет достаточно выделенного инициализатора суперкласса.
  3. initWithCoder:/encodeWithCoder: Используется при десериализации, сериализации.
  4. awakeFromNib. Посылается всем объектам, загружаемым из nib-файла, но после того, как будут созданы все экземпляры объектов. Это гарантирует, что можно будет установить все указатели IBOutlet для данного объекта.
  5. dealloc or finalize. Деструкторы.

Объект приложения NSApp посылает сообщения

  • applicationDidFinishLaunching:,
  • applicationWillFinishLaunching:,
  • applicationWillTerminate:

своему объекту-делегату, который может обработать эти ситуации.

Initialize or Decode?

Для сериализации/десериализации (архивирования, разархивирования) объект должен реализовать протокол NSCoding. При разархивировании ему будет послано сообщение initWithCoder:. Если объект нужно инициализировать не только при разархировании, то нужно написать обрабочик и для init.

Исключение: подкласс для класса фреймворка, связанный с Object в IB. В этом случае, экземпляру будет послано init, поэтому лучше всю инициализацию делать вообще в awakeFromNib.

Storing and Accessing Properties

Свойства могут быть двух видов: атрибуты и отношения.

Свойства могут  хранить экземпляры (т.е. не обязательно хранят).

Taking Advantage of Declared Properties

Возможны два способа работы со свойствами: в случае использования garbage collector (1) и в случае memory-managed code (2).

2: В свойствах setter должны быть заданы атрибуты retain или copy. При использовании NSCopying всегда нужно указывать copy.


1: Генерируются setter для name и getter для accountID.


@interface MyClass : NSObject {
NSString *name;
NSNumber *accountID;
}
@property (copy) NSString *name;
@property (readonly) NSNumber *accountID;
// ...
@end
@implementation MyClass
@synthesize name, accountID;
// ...
@end


2: Для currentHost в getter будет увеличивать счетчик ссылок. Для hidden будет сгенерирован getter с заданным именем.


@interface MyClass : NSObject {
NSHost *currentHost;
Boolean *hidden;
}
@property (retain, nonatomic) NSHost *currentHost;
@property (getter=isHidden, nonatomic) Boolean *hidden;
// ...
@end
@implementation MyClass
@synthesize name, hidden;
// ...
@end

Implementing Accessor Methods

Методы getter и setter должны себя вести в соответствие с требованиями к управлению памятью. Setter должен позаботиться об освобождении памяти, занятой предыдущим значением.

Правила:

  • Полученный из getter объект можно безопасно использовать в области видимости вызвавшего getter метода. 
  • Вызывающий getter метод не должен релизить полученный объект в своей области видимости, не отправив ему предварительно retain или copy.


- (NSString *)title {
return title;
}
- (void)setTitle:(NSString *)newTitle {
if (title != newTitle) {
[title autorelease];
title = [newTitle copy];
}
}


В примере выше возможна опасная ситуация: клиент получит указатель на title и почти одновременно с этим будет вызван setter, который авторелизнет title. Через некоторое время пул будет очищен и title перестанет существовать. Клиент останется при этом с недействительным указателем.


Вариант лучше:


- (NSString *)title {
return [[title retain] autorelease];
}
- (void)setTitle:(NSString *)newTitle {
if (title != newTitle) {
[title release];
title = [newTitle copy];
}
}


Счетчик ссылок на title в getter-е увеличен на 1 и title будет послано 1 сообщение release, когда будет чиститься пул. Поэтому клиент останется с действительным указателем даже после [title release] в setter-е.


Когда посылать retain, а когда copy при передаче объектов?

Для этого нужно ответить на вопрос: что нужно получить, сам объект как таковой или его значение? Сам объект возможно изменит своё значение, но это не страшно, если нам нужна именно сущность. Конкретное значение, например, строки вероятно более важно, чем просто какая-нибудь строка.

Для setter-а можно рассуждать так. Если он устанавливает значение атрибута объекта, т.е. некое примитивное значение типа строки, числа или цвета, то это значение нужно скопировать из передаваемого параметра. Если же устанавливается отношение, т.е. отношение с другим объектом, то скорее всего нужно послать параметру retain.

Если придерживать "ленивого" использования атрибутов, т.е. вызывать getter только, когда атрибут понадобится, то его значение нужно устанавливать также в этом getter. Т.е. setter нельзя вызывать явно.

среда, 22 апреля 2009 г.

22.04.09. Наследование от классов Cocoa

Adding Behavior to a Cocoa Program

Основной координирующий объект приложения - NSApplicationMain. Он получает события, находит для них объекты-обработчики и так по кругу. 

Последовательность такая: объект создается, устанавливается autorealese пул, загружается интерфейс из nib-файла, управление передается пользователю, т.е. обрабатываются внешние события.

Отличие библиотеки от фреймворка. Требуемая функциональность из библиотеки может быть получена напрямую, без создания дополнительной инфраструктуры объектов. При использовании фреймворка код приложения дополняет фреймворк, при использовании библиотеки код библиотеки дополняет приложение.

Фреймворк задает структуру и модель приложения. Для решения каждой задачи придется использовать столько классов, сколько предполагает фреймворк. 

Вызовы из библиотеки можно рассматривать как использование независимых друг от друга сервисов.

В Cocoa есть классы, предоставляющие услуги

  • напрямую,
  • предназначенные для выполнения работы за кадром,
  • в обобщенном виде (требуют конкретной реализации),
  • через делегирование и уведомления.

Класс, допускающий делегирование, имеет протокол (интерфейс), методы которого могут реализовать делегаты. Делегат должен при этом зарегистрироваться у делегирующего класса.

Класс может опубликовать список уведомлений, которые он может рассылать всем заинтересованным объектам-подписчикам.

Делегаты и уведомления являются способами встраивания своего кода в фреймворк.

Cocoa API Conventions (некоторые)

Методы Cocoa могут возвращать nil, булевское значение, а также генерировать исключение при возникновении ошибок. Для ошибки может передаваться специальный аргумент. 

Строки могут быть литералами (начинаются с @"") или константами. Предпочтительно использовать константные строки.

Метод, возвращающий значение атрибута, должен иметь имя атрибута, устанавливающий - начинаться с set, а затем имя атрибута с большой буквы.

Inheriting From a Cocoa Class

Значительная часть фреймворка имеет обобщенный вид (абстрактные классы). 

В Cocoa часто происходит так: метод моего класса вызывает определенный метод фреймворка, а он в свою очередь вызывает какой-то свой метод, переопределенный в моем классе.



Types of Overridden Methods

Готовым поведением можно воспользоваться, а можно переопределить его у себя в подклассе.

Некоторые методы имеют реализацию по умолчанию, возвращающую некоторое значение (YES или NO). Это значение определяет, включена или выключена некоторая возможность. Можно переопределить этот метод, чтобы переключить возможности.

Функциональность некоторых методов базового класса нужна в порожденном классе. В перекрываемом методе происходит дополнение базовой функциональности. Для этого порожденный метод отправляет сообщение родителю super, вызывающее одноименный с ним родительский метод.

Ну и конечно при обычном раскладе super-у сообщение посылать не нужно, нужно просто переопределить метод базового класса.

When to Make a Subclass

Нужно определить необходимый базовый класс (например, для MVC).

Если нужно добавить пару методов к существующему классу, то проще воспользоваться категорией.

Также не стоит забывать о возможности композиции с одновременным наследованием от того же класса.

21.04.09. Изменяемость. Class Clusters. Категории, протоколы.

Cocoa - Изменяемость (mutability) объектов 

Некоторые классы неизменяемые, производные от них - изменяемые. 

Иногда предпочтительнее создать новый объект, чем изменять старый. 

Неизменяемые (immutable) лучше сделать, когда  данные объекта будут меняться значительно (NSString,NSData). 

Изменяемые (mutable) данные объекта будут меняться постепенно (массивы, коллекции). 

Изменяемые коллекции можно делать неизменяемыми сделав копию. Копия по умолчанию будет неизменяемая. Ее можно отдать клиенту. 

При передаче клиенту объекта, о котором важно знать изменяемый он или нет, нужно передавать вместе с ним флаг. 

Могут быть проблемы, если хранить изменяемые объекты в коллекциях 

Cocoa - Class Clusters 

Кластеры объединяют классы, производные как private от абстрактного класса. В абстрактном классе есть метод создания экземпляра нужного подкласса. 

В абстрактном классе есть методы-примитивы, которые обязательны для переопределения в производных классах. Эти методы обращаются к данным производного класса. 

Остальные методы реализуются через методы-примитивы. 

Composite Object - декоратор. 

Cocoa - Категории, протоколы 
Категория - способ добавить методы к классу не создавая подклассов 

По категориям удобно разделить различные аспекты класса 

Категории можно назначить и NSObject. Добавленные методы будут присутствовать у всех объектов в программе.  

Неформальный протокол - это категория NSObject. Необязательно реализовывать все методы неформального протокола. Проверка реализации осуществляется через respondsToSelector:  

Протокол - аналог интерфейса 

Множественное наследование протоколов 

Проверка реализованности протокола у объекта