Создание подкласса NSDocument
NSDocument
подкласс обеспечивает хранение для модели и возможности загрузить и сохранить данные документа. Это также имеет любые выходы и действия, требуемые для пользовательского интерфейса. NSDocument
объект автоматически создает NSWindowController
объект управлять тем файлом пера, но NSDocument
объект служит объектом прокси Владельца Файла для файла пера.
Когда Вы разделяете на подклассы NSDocument
, необходимо переопределить определенные ключевые методы и реализовать других, чтобы сделать, по крайней мере, следующие вещи:
Считайте данные существующих документов от файлов
Запишите данные документа в файлы
Инициализируйте новые документы
Поместите документы в iCloud и удалите их
В частности необходимо переопределить чтение того и один метод записи. В самом простом случае можно переопределить основанное на данных чтение и методы записи, readFromData:ofType:error:
и dataOfType:error:
.
Чтение данных документа
Открытие существующих документов, хранивших в файлах, является одной из наиболее распространенных операций, которые выполняют основанные на документе приложения. Ответственность Вашего переопределения состоит в том, чтобы загрузить данные файла в модель данных Вашего приложения.
Если это работает на Ваше приложение, необходимо переопределить основанный на данных метод чтения, readFromData:ofType:error:
. Переопределение того метода делает Вашу работу проще, потому что это использует читающую документ инфраструктуру по умолчанию, предоставленную NSDocument
, который может обработать многократные случаи от Вашего имени.
Как переопределить основанный на данных метод чтения
Можно переопределить readFromData:ofType:error:
метод для преобразования NSData
объект, содержащий данные документа во внутренние структуры данных и дисплей документа, что данные в окне документа. Вызовы архитектуры документа readFromData:ofType:error:
, передача в NSData
объект, во время его процесса инициализации документа.
Перечисление 4-1 показывает реализацию в качестве примера readFromData:ofType:error:
читающий документ метод. Этот пример предполагает, что приложение имеет NSTextView
объект, сконфигурированный с NSTextStorage
возразите для содержания текстовых данных представления. NSDocument
объект имеет a setMString:
метод доступа для документа NSAttributedString
модель данных, объявленную как свойство, называют mString
.
Перечисление 4-1 Основанная на данных читающая документ реализация метода
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName |
error:(NSError **)outError { |
BOOL readSuccess = NO; |
NSAttributedString *fileContents = [[NSAttributedString alloc] |
initWithData:data options:NULL documentAttributes:NULL |
error:outError]; |
if (!fileContents && outError) { |
*outError = [NSError errorWithDomain:NSCocoaErrorDomain |
code:NSFileReadUnknownError userInfo:nil]; |
} |
if (fileContents) { |
readSuccess = YES; |
[self setMString:fileContents]; |
} |
return readSuccess; |
} |
Если необходимо иметь дело с расположением файла, переопределите чтение URL и методы записи вместо этого. Если Ваше приложение должно управлять файлами документов, которые являются пакетами файла, переопределяют чтение обертки файла и методы записи вместо этого. Для получения информации о переопределении ОСНОВАННЫХ НА URL и основанных на файле-оберткой методов чтения посмотрите Переопределение URL и Методов Чтения Пакета Файла.
Поток сообщений во время считывания данных документа показан на рисунке 6-5.
Просто поддерживать параллельное открытие документа
Метод класса NSDocument
, canConcurrentlyReadDocumentsOfType:
, включает Ваш NSDocument
разделите на подклассы для загрузки документов одновременно, с помощью фоновых потоков. Это переопределение позволяет параллельное чтение многократных документов и также позволяет приложению быть быстро реагирующим при чтении большого документа. Можно переопределить canConcurrentlyReadDocumentsOfType:
возвратиться YES
включить эту возможность. Когда Вы делаете, initWithContentsOfURL:ofType:error:
выполняется на фоновом потоке когда вводные файлы через Открытое диалоговое окно или от Средства поиска.
Реализация по умолчанию этого метода возвраты NO
. Переопределение подкласса должно возвратиться YES
только для типов документов, чтение которых кода может быть безопасно выполнено одновременно на неосновных потоках. Если тип документа полагается на информацию об общем состоянии, необходимо возвратиться NO
для того типа.
Не полагайтесь на методов get свойства документа в переопределениях чтения методов
Не вызывать fileURL
, fileType
, или fileModificationDate
из Ваших переопределений. Во время чтения, обычно происходящего во время объектной инициализации, нет никакой гарантии этого NSDocument
свойства как расположение или тип файла были установлены все же. Ваш переопределенный метод должен быть в состоянии определить все, что он должен сделать чтение от передаваемых параметров. Во время записи Ваш документ можно попросить записать его содержание в различное расположение или использование различного типа файла.
Если Ваше переопределение не может определить всю информацию, этому нужно от передаваемых параметров, рассмотрите переопределение другого метода. Например, если Вы видите потребность вызвать fileURL
из переопределения readFromData:ofType:error:
, необходимо вместо этого переопределить readFromURL:ofType:error:
и используйте переданный - в значении URL.
Запись данных документа
В дополнение к реализации читающего документ метода необходимо реализовать метод записи документа сохранить данные документа на диск. В самом простом случае можно переопределить основанный на данных метод записи, dataOfType:error:
. Если это работает на Ваше приложение, необходимо переопределить dataOfType:error:
. Переопределение того метода делает Вашу работу проще, потому что это использует читающую документ инфраструктуру по умолчанию, предоставленную NSDocument
. Ответственность Вашего переопределения dataOfType:error:
метод должен создать и возвратить данные документа поддерживаемого типа, упакованного как NSData
объект, в подготовке к записи, что данные к файлу.
Перечисление 4-2 показывает реализацию в качестве примера dataOfType:error:
. Как с соответствующим методом чтения документа реализации в качестве примера, этот пример предполагает, что приложение имеет NSTextView
объект, сконфигурированный с NSTextStorage
возразите для содержания данных документа. Объекту документа подключили свойство выхода с NSTextView
возразите и названный textView
. Объект документа также синтезировал mString
и setMString:
средства доступа для документа NSAttributedString
модель данных, объявленную как свойство, называют mString
.
Перечисление 4-2 Основанная на данных реализация метода записи документа
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError { |
NSData *data; |
[self setMString:[self.textView textStorage]]; // Synchronize data model with the text storage |
NSMutableDictionary *dict = [NSDictionary dictionaryWithObject:NSRTFTextDocumentType |
forKey:NSDocumentTypeDocumentAttribute]; |
[self.textView breakUndoCoalescing]; |
data = [self.mString dataFromRange:NSMakeRange(0, [self.mString length]) |
documentAttributes:dict error:outError]; |
if (!data && outError) { |
*outError = [NSError errorWithDomain:NSCocoaErrorDomain |
code:NSFileWriteUnknownError userInfo:nil]; |
} |
return data; |
} |
Переопределение отправляет NSTextView
объект a breakUndoCoalescing
обменивайтесь сообщениями при сохранении текстового содержания представления для сохранения надлежащего отслеживания несохраненных изменений и грязного состояния документа.
Если для Вашего приложения нужен доступ к файлам документов, можно переопределить writeToURL:ofType:error:
вместо этого. Если Ваши данные документа хранятся в пакетах файла, можно переопределить fileWrapperOfType:error:
вместо этого. Для получения информации о переопределении другого NSDocument
методы записи, посмотрите Переопределение URL и Методов записи Пакета Файла.
Фактический поток сообщений во время этой последовательности событий показан подробно на рисунке 6-6.
Инициализация нового документа
init
метод NSDocument
определяемый инициализатор, и он вызывается другими инициализаторами initWithType:error:
и initWithContentsOfURL:ofType:error:
. Если Вы выполняете инициализации, которые должны быть сделаны при создании новых документов, но не при открытии существующих документов, переопределения initWithType:error:
. Если у Вас есть какие-либо инициализации, применяющиеся только к документам, открытым, переопределение initWithContentsOfURL:ofType:error:
. Если у Вас есть общие инициализации, переопределение init
. Во всех трех случаях, убедиться вызвать реализацию суперкласса как первое действие.
Если Вы переопределяете init
, удостоверьтесь, что никогда не возвращается Ваше переопределение nil
. Возврат nil
мог вызвать катастрофический отказ (в некоторых версиях AppKit) или представить меньше, чем полезное сообщение об ошибке. Если, например, Вы хотите предотвратить создание или открытие документов при обстоятельствах, уникальных для Вашего приложения, переопределите определенное NSDocumentController
метод вместо этого. Т.е. необходимо управлять этим поведением непосредственно в логике уровня приложения (для предотвращения создания документа или открывающийся в определенных случаях) вместо того, чтобы ловить ситуацию после того, как уже началась инициализация документа.
Реализация awakeFromNib
инициализировать объекты, разархивированные от файлов пера окна документа (но не сам документ).
Движущиеся Данные Документа к и от iCloud
Технология хранения iCloud позволяет Вам совместно использовать документы и другие данные приложения среди многократных компьютеров, выполняющих Ваше основанное на документе приложение. Если у Вас есть версия iOS Вашего основанного на документе приложения, совместно использующего те же форматы данных документа, документы могут быть совместно использованы среди устройств на iOS также, как показано на рисунке 4-1. Изменения, внесенные в файл или каталог на одном устройстве, сохранены локально и затем продвинуты к iCloud с помощью локального демона. Передача файлов к и от каждого устройства очевидна для Вашего приложения.
Доступом к iCloud управляют с помощью прав, которые приложение конфигурирует через XCode. Если эти права не присутствуют, Вашему приложению препятствуют получить доступ к файлам и другим данным в iCloud. В частности контейнерные идентификаторы для Вашего приложения должны быть объявлены в com.apple.developer.ubiquity-container-identifiers
право. Для получения информации о том, как сконфигурировать права Вашего приложения, посмотрите Разработку для App Store и Руководство по Потоку операций Инструментов для Mac.
Всеми файлами и каталогами, сохраненными в iCloud, должен управлять объект, принимающий NSFilePresenter
протокол и все изменения, которые Вы вносите в те файлы и каталоги, должны произойти через NSFileCoordinator
объект. Предъявитель файла и координатор файла препятствуют тому, чтобы внешние источники изменили файл одновременно, и поставляют соответствующие уведомления другим предъявителям файла. NSDocument
реализует методы NSFilePresenter
протокол и дескрипторы все связанное с файлом управление для Вас. Все Ваше приложение должно сделать, читается, и запишите данные документа, когда сказали, чтобы сделать так. Убедитесь, что Вы переопределяете autosavesInPlace
возвратиться YES
включить координацию файла в Вашем NSDocument
объект.
Определение, Включен ли iCloud
Рано в выполнении Вашего приложения, прежде чем Вы попытаетесь использовать любые другие интерфейсы iCloud, необходимо вызвать NSFileManager
метод URLForUbiquityContainerIdentifier:
определить, включено ли хранение iCloud. Этот метод возвращает допустимый URL, когда iCloud включен (и указанный контейнерный каталог доступен), или nil
когда отключен iCloud. URLForUbiquityContainerIdentifier:
также возвраты nil
при указании контейнерного ID, к которому приложению не позволяют получить доступ, или это не существует. В этом случае, NSFileManager
возразите регистрирует сообщение к консоли, чтобы помочь диагностировать ошибку.
Перечисление 4-3 иллюстрирует, как определить, включен ли iCloud для файла документа URL, представив сообщение об ошибке пользователю в противном случае и установив значение целевого URL документа к тому из его контейнера iCloud иначе (в подготовке к перемещению документа iCloud с помощью setUbiquitous:itemAtURL:destinationURL:error:
метод).
Перечисление 4-3 , Определяющее, включен ли iCloud
NSURL *src = [self fileURL]; |
NSURL *dest = NULL; |
NSURL *ubiquityContainerURL = [[[NSFileManager defaultManager] |
URLForUbiquityContainerIdentifier:nil] |
URLByAppendingPathComponent:@"Documents"]; |
if (ubiquityContainerURL == nil) { |
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: |
NSLocalizedString(@"iCloud does not appear to be configured.", @""), |
NSLocalizedFailureReasonErrorKey, nil]; |
NSError *error = [NSError errorWithDomain:@"Application" code:404 |
userInfo:dict]; |
[self presentError:error modalForWindow:[self windowForSheet] delegate:nil |
didPresentSelector:NULL contextInfo:NULL]; |
return; |
} |
dest = [ubiquityContainerURL URLByAppendingPathComponent: |
[src lastPathComponent]]; |
Поскольку сообщение указывает nil
для контейнерного параметра идентификатора, URLForUbiquityContainerIdentifier:
возвращает первый контейнер, перечисленный в com.apple.developer.ubiquity-container-identifiers
право и создает соответствующий каталог, если это еще не существует. Также Вы могли указать контейнерный идентификатор своего приложения — связь команды ID и комплект приложений ID, разделенный периодом для основного контейнерного идентификатора приложения или различного контейнерного каталога. Например, Вы могли объявить строковую константу для контейнерного идентификатора, как в следующем примере, и передать постоянное имя с сообщением.
static NSString *UbiquityContainerIdentifier = @"A1B2C3D4E5.com.domainname.appname"; |
Метод также добавляет имя файла документа к целевому URL.
Поиск Документов в iCloud
Приложения должны использовать NSMetadataQuery
объекты искать элементы в каталогах контейнера iCloud. Метаданные запрашивают результаты возврата только, когда хранение iCloud включено, и соответствующие контейнерные каталоги были созданы. Для получения информации о том, как создать и сконфигурировать поисковые запросы метаданных, видит Руководство по программированию Поиска Метаданных Файла. Для получения информации о том, как выполнить итерации использования каталогов NSFileManager
, см. Руководство по программированию Файловой системы.
Перемещение Документа в Хранение iCloud
Для сохранения нового документа к каталогу контейнера iCloud сначала сохраните его локально и затем вызовите NSFileManager
метод setUbiquitous:itemAtURL:destinationURL:error:
перемещать файл документа в iCloud.
Перечисление 4-4 показывает реализацию в качестве примера метода, перемещающего файл в хранение iCloud. Это принимает источник и целевой URLs от Перечисления 4-3.
Перечисление 4-4 , Перемещающее документ iCloud
dispatch_queue_t globalQueue = |
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
dispatch_async(globalQueue, ^(void) { |
NSFileManager *fileManager = [[NSFileManager alloc] init]; |
NSError *error = nil; |
// Move the file. |
BOOL success = [fileManager setUbiquitous:YES itemAtURL:src |
destinationURL:dest error:&error]; |
dispatch_async(dispatch_get_main_queue(), ^(void) { |
if (! success) { |
[self presentError:error modalForWindow:[self windowForSheet] |
delegate:nil didPresentSelector:NULL contextInfo:NULL]; |
} |
}); |
}); |
[self setFileURL:dest]; |
[self setFileModificationDate:nil]; |
После того, как файл документа был перемещен в iCloud, как показано в Перечислении 4-4, читение и писание выполняются нормальным NSDocument
механизмы, автоматически управляющие координацией доступа к файлу, требуемой iCloud.
Удаление Документа от Хранения iCloud
Для перемещения файла документа из каталога контейнера iCloud выполните ту же процедуру, описанную в Перемещении Документа в Хранение iCloud, кроме переключателя источник URL (теперь файл документа в каталоге контейнера iCloud) и целевой URL (расположение файла документа в локальной файловой системе). Кроме того, первый параметр setUbiquitous:itemAtURL:destinationURL:error:
метод должен теперь быть NO
.
Для ясности в этом примере называют URL файла в хранении iCloud cloudsrc
и локальный URL, в который перемещен файл, называют localdest
.
dispatch_queue_t globalQueue = |
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
dispatch_async(globalQueue, ^(void) { |
NSFileManager *fileManager = [[NSFileManager alloc] init]; |
NSError *error = nil; |
// Move the file. |
BOOL success = [fileManager setUbiquitous:NO itemAtURL:cloudsrc |
destinationURL:localdest error:&error]; |
dispatch_async(dispatch_get_main_queue(), ^(void) { |
if (! success) { |
[self presentError:error modalForWindow:[self windowForSheet] |
delegate:nil didPresentSelector:NULL contextInfo:NULL]; |
} |
}); |
}); |
Для получения дополнительной информации о iCloud, см. Руководство по проектированию iCloud.
NSDocument обрабатывает разрешение конфликтов среди версий документа
NSDocument
разрешение конфликтов дескрипторов автоматически, таким образом, Вы не должны реализовывать его сами. В то время как документ открыт, если входит конфликт NSDocument
представляет лист, прося, чтобы пользователь разрешил конфликт (или проигнорировать, который отмечает его, как разрешено и принимает автоматического победителя конфликта, обычно тот с новой датой модификации). Щелчок по Resolve вызывает пользовательский интерфейс Версий (см., что Пользователи Могут Просмотреть Версии документа) с только конфликтными видимыми версиями. Пользователь может выбрать определенную версию и нажать Restore, чтобы сделать его победителем конфликта, или просто выбрать Done для принятия автоматического победителя.
Даже после того, как конфликт разрешен, NSDocument
всегда сохраняет конфликтные версии, и к ним можно обычно получать доступ через Версии.
Дополнительные переопределения метода
Области, описанные элементами в следующих разделах, требуют переопределений метода в некоторых ситуациях. И, конечно, необходимо реализовать любые методы, которые являются особенными для Вашего NSDocument
подкласс. Больше опций для Вашего NSDocument
подкласс описан в Альтернативных Конструктивных соображениях.
Создание контроллера окна
NSDocument
подклассы должны создать свои контроллеры окна. Они могут сделать это косвенно или непосредственно. Если документ имеет только один файл пера с одним окном в нем, подкласс может переопределить windowNibName
возвратить имя файла пера окна. Как следствие архитектура документа создает значение по умолчанию NSWindowController
экземпляр для документа, с документом как владелец файла пера. Если документ имеет многократные окна, или если экземпляр пользовательского NSWindowController
подкласс используется, NSDocument
подкласс должен переопределить makeWindowControllers
создать эти объекты.
Если Ваш документ имеет только одно окно, шаблон проекта обеспечивает реализацию по умолчанию NSDocument
метод windowNibName
:
- (NSString *)windowNibName { |
return @"MyDocument"; |
} |
Если у Вас есть пользовательский подкласс, если Ваш документ имеет больше чем одно окно, или NSWindowController
, переопределение makeWindowControllers
вместо этого. Удостоверьтесь, что Вы добавляете каждый создаваемый контроллер окна к списку таких объектов, которыми управляет использование документа addWindowController:
.
Загрузка файла пера окна
Можно реализовать windowControllerWillLoadNib:
и windowControllerDidLoadNib:
выполнять любые необходимые задачи имело отношение к окну прежде и после того, как оно загружается из файла пера. Например, Вы, возможно, должны выполнить операции установки на объектах пользовательского интерфейса, таких как установка содержания представления, после того, как были загружены данные модели приложения. В этом случае необходимо помнить что NSDocument
методы считывания данных, такой как readFromData:ofType:error:
, вызываются, прежде чем объекты пользовательского интерфейса документа, содержавшиеся в его файле пера, загружаются. Конечно, Вы не можете отправить сообщения в объекты пользовательского интерфейса до окончания загрузок файла пера. Так, можно выполнить в таких операциях windowControllerDidLoadNib:
.
Вот пример:
- (void)windowControllerDidLoadNib:(NSWindowController *)windowController { |
[super windowControllerDidLoadNib:windowController]; |
[textView setAllowsUndo:YES]; |
if (fileContents != nil) { |
[textView setString:fileContents]; |
fileContents = nil; |
} |
} |
Печать и макет страницы
Основанное на документе приложение может изменить информацию, которую оно использует, чтобы определить, как распечатаны данные документа. Эта информация инкапсулируется в NSPrintInfo
объект. Если Вы хотите, чтобы пользователи были в состоянии распечатать документ, необходимо переопределить printOperationWithSettings:error:
, возможно обеспечение измененного NSPrintInfo
объект.
Изменение диалогового представления аксессуара сохранения
По умолчанию, когда NSDocument
выполняет диалоговое окно Сохранения, и документ имеет многократные перезаписываемые типы документов, это вставляет вспомогательное представление около нижней части диалогового окна. Это представление содержит всплывающее меню перезаписываемых типов. Если Вы не хотите это всплывающее меню, переопределение shouldRunSavePanelWithAccessoryView
возвратиться NO
. Можно также переопределить prepareSavePanel:
сделать дальнейшую настройку диалогового окна Сохранения.
Проверка пунктов меню
NSDocument
реализации validateUserInterfaceItem:
управлять включенным состоянием пунктов меню Revert Document и Save. Если Вы хотите проверить другие пункты меню, Вы можете переопределить этот метод, но, несомненно, вызовете реализацию суперкласса. Для получения дополнительной информации о проверке пункта меню посмотрите Меню приложения и Раскрывающийся Список, Программируя Темы.