Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 如何在XCode 4中使用NSDocumentController的子类?_Objective C_Cocoa_Xcode4 - Fatal编程技术网

Objective c 如何在XCode 4中使用NSDocumentController的子类?

Objective c 如何在XCode 4中使用NSDocumentController的子类?,objective-c,cocoa,xcode4,Objective C,Cocoa,Xcode4,我目前正在努力自学可可发展。为此,我买了一本非常优秀的书,MacOSX的可可食谱:佛蒙特食谱,这本书介绍了如何创建一个示例应用程序。这很好,但它是针对XCode 3.2编写的,而不是针对XCode 4编写的。到目前为止,我自己已经能够解决这个问题,但我遇到了一个问题,我不知道如何遵循指示 从本质上讲,本书介绍了子类化NSDocumentController的示例,这样应用程序就可以处理两种(最终可能是任意数量)不同类型的文档,并为每种类型打开相应的窗口。因此,我已经创建了NSDocumentCo

我目前正在努力自学可可发展。为此,我买了一本非常优秀的书,MacOSX的可可食谱:佛蒙特食谱,这本书介绍了如何创建一个示例应用程序。这很好,但它是针对XCode 3.2编写的,而不是针对XCode 4编写的。到目前为止,我自己已经能够解决这个问题,但我遇到了一个问题,我不知道如何遵循指示

从本质上讲,本书介绍了子类化
NSDocumentController
的示例,这样应用程序就可以处理两种(最终可能是任意数量)不同类型的文档,并为每种类型打开相应的窗口。因此,我已经创建了
NSDocumentController
(这本书称之为
VRDocumentController
)的一个自定义子类,现在我需要使其在应用程序启动过程中相对较早地加载此控制器的实例。基本上,这个类是一个单例类,所以我必须在应用程序实例化标准类之前实例化我的类,这必须在过程的早期完成。很公平

该书引用了,指出有两种方法可以解决这个问题:在
主菜单.xib
文件中实例化类,或者在
-applicationWillFinishLaunching:
委托方法中实例化一个类。苹果的文档并没有明确说明如何做这两件事(稍后会有更多介绍),本书只介绍了第一个版本,我认为这可能是我首选的方法

我的问题是:我无法在XCode 4中实现这一点。这本书为XCode 3.2提供的说明不再准确,因为Interface Builder现在已经被移植到XCode中,新版本的“类选项卡”不再显示我的项目的类。我发现我问了一个类似的问题,所以我试着按照公认的答案回答。但是,当我打开Identity Inspector并尝试键入
VRDocumentController
时,它只会向我发出嘟嘟声,不会接受它。我编写的其他控制器类似乎都不是可接受的输入


我也很乐意走另一条路;在
-applicationWillFinishLaunching
方法中实例化副本。但是,我根本不知道该方法实际上属于哪个类,或者它的返回类型是什么。我也进行了大量的搜索,但运气不佳。

在您的应用程序委托中:

// LukeAppDelegate.h
#import "LukeAppDelegate.h"
#import "VRDocumentController"

- (void)applicationWillFinishLaunching:(NSNotification *)notification {
    VRDocumentController *dc = [[VRDocumentController alloc] init];
}
这将确保创建
VRDocumentController
的实例并将其注册为共享文档控制器,防止Cocoa使用默认的
NSDocumentController

至于为什么不能在nib文件中使用自定义对象,请确保在将新对象拖动到nib文件中时选择对象(蓝色立方体)而不是对象控制器(绿色球体中的蓝色立方体)


编辑:如果您的目标是支持恢复的OS X版本,
-applicationWillFinishLaunching:
可能太晚,无法注册自定义文档控制器。如果应用程序委托放置在MainMenu.xib中,则在还原任何文档之前,应通过nib加载过程对其进行实例化,因此您可以将
NSDocumentController
子类初始化移动到应用程序委托的init方法:

// LukeAppDelegate.h
#import "LukeAppDelegate.h"
#import "VRDocumentController"

- (id)init {
    self = [super init];
    VRDocumentController *dc = [[VRDocumentController alloc] init];
    return self;
}
//这似乎是发布此消息的最佳线程。
//这是NSDocument/NSEndomanager使用的不完整的框架示例。
//使用风险自负
//最终,如果苹果审查/纠正了这一点,并在他们的文档中添加了正确的示例代码,我会很高兴的。
//我将伪代码放在实现特定的位置:
//“……具体实施……”
//“…实现一些固有的危险…从文件加载数据”
//“…实现一些固有的危险…将更改提交到文件…”
//“…在我的实现中,当编辑窗口关闭时,我用保存/退出选项提示用户…”
//
//苹果的文档说明NSDocumentController应该*很少*子类化,
//但是苹果没有提供示例代码来完成您所需要的。
//在尝试将NSDocumentController子类化之后,我决定删除所有代码。
//就我而言,我真正需要的是:
//@interface NSObject(NSApplicationLegate)
//-(BOOL)应用程序:(NSApplication*)发送方打开文件:(NSString*)文件名
//
//简而言之,我的要求是:
//NSNDOManager,一次只允许打开一个文档(目前),一个文档有多个编辑窗口,
//自动保存到临时文件,无需保存即可退出并重新启动,
//自定义“打开”、“保存”、“另存为”、“还原为已保存”等。。。MyDocumentController
//下面的子类提供了一种绕过大部分NSDocumentController的简单方法
//文件对话框,但仍使用NSDocument的其他功能(如跟踪未保存的更改)。
//仅覆盖“NSDocumentController documentForURL”。
/*必须在Info.plist中定义NSDocument子类。
如果您不这样做,Lion将发出如下NSLog消息:
-[NSDocumentController openDocumentWithContentsOfURL:display:completionHandler:]在状态还原期间失败。以下是错误:
错误域=NSCOCAERRORDOMAIN Code=256“无法打开文档”blah.myfiletype。无法打开“blah document”格式的文件。”
在本例中,类型为.myfiletype的文件与我的NSDocument子类关联
就个人而言,我讨厌在Info.plist中输入类名,因为它不可维护!
苹果开发者注意:如果你想强迫我们这么做,请让我们做
确保在“项目”中搜索“MyDocument”的XCode在plist文件中找到条目!
哇,我刚刚回答了我自己的问题:苹果!请将“所有候选文件”设置为t
// This looked like the best thread to post this.
// This is incomplete skeleton example of NSDocument/NSUndoManager usage.
// Use at your own risk
// Ultimately, would love if Apple reviewed/corrected this and added correct sample code to their docs.
// I put pseudo code in implementation specific spots:
//    "... implementation specific ..."
//    "... implement something inherently dangerous... loading your data from a file"
//    "... implement something inherently dangerous... commit changes to a file ..."
//    "... in my implementation, I prompt user with save/quit options when edit window is closed ..."
//

// Apple's documentation states that NSDocumentController should *rarely* be subclassed, 
// but Apple fails to provide sample code to accomplish what you need.
// After trying to subclass NSDocumentController, I decided to rip all that code out.
// What I really needed, in my case, was this:
//    @interface NSObject(NSApplicationDelegate)
//    - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
//
// My requirements in a nutshell:
// NSUndoManager, only allow 1 document open at a time (for now), multiple editing windows for 1 document, 
// automatically save to temporary file, ability to quit and relaunch without saving,
// Custom "open", "save", "save as", "revert to saved", etc... MyDocumentController
// subclass below provides a simple way to bypass much of the NSDocumentController
// file dialogs, but still use other features of NSDocument (like track unsaved changes).
// Override only "NSDocumentController documentForURL".

/* your NSDocument subclass MUST be defined in your Info.plist.
    If you don't, Lion will complain with a NSLog message that looks like this:
  -[NSDocumentController openDocumentWithContentsOfURL:display:completionHandler:] failed during state restoration. Here's the error:
   Error Domain=NSCocoaErrorDomain Code=256 "The document “blah.myfiletype” could not be opened.  cannot open files in the “blah Document” format." 
In this example, files of type .myfiletype are associated with my NSDocument subclass
Personally, I hate having to put a class name in my Info.plist, because its not maintainable!
Note to Apple developers: if you are going to force us to do this, then pleasssse make
sure that an XCode search "In Project" for "MyDocument" finds the entry in the plist file!
Oh wow, I just answered my own question: Apple! please make "All candidate files" the
default option for search!

MyDocument:
<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>My Funky App File</string>
        <key>NSDocumentClass</key>
        <string>MyDocument</string>
        <key>CFBundleTypeExtensions</key>
        <array>
            <string>myfiletype</string>
        </array>
        <key>CFBundleTypeIconFile</key>
        <string>My_File_Icon.icns</string>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
    </dict>
</array>
*/
#define kMyFileTypeExtension @"myfiletype"

@interface MyDocument : NSDocument
{
}
- (void) registerForUndoGrouping;
- (NSString *)filePath; // convenience function
- (void) setFilePath: (NSString *)filePath; // convenience function

+ (BOOL) openMyDocument: (NSString *)filename; // class method
@end

extern int gWantsToQuit; // global indicator that it's time to stop drawing/updating

// track my startup state so I can control order of initialization, and so I don't
// waste time drawing/updating before data is available.
enum // MyLaunchStatus
{
   kFinishedPreWaking       = 0x01, // step 1: applicationWillFinishLaunching called
   kFinishedOpenFile        = 0x02, // step 2: (optionally) application:openFile: called (important for detecting double-click file to launch app)
   kFinishedWaking          = 0x04, // step 3: NSApp run loop ready to run. applicationDidFinishLaunching
   kFinishedPreLaunchCheck  = 0x08, // step 4: error recovery check passed
   kFinishedLoadingData     = 0x10, // step 5: data loaded
   kFinishedAndReadyToDraw  = 0x20, // step 6: run loop ready for drawing

};

typedef NSUInteger MyLaunchStatus;

#pragma mark -
@interface MyAppController : NSResponder <MidiProtocol, NSOpenSavePanelDelegate, NSTextFieldDelegate, NSWindowDelegate>
MyDocument *toDocument;
@end



#pragma mark -
@implementation MyDocument

- (id)init
{
   if ( !(self = [super init]) ) return self;
   return self;
}

- (void) registerForUndoGrouping
{


   [[NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(beginUndoGroup:) 
                                                name:NSUndoManagerDidOpenUndoGroupNotification 
                                              object:nil]; 
}

- (void)canCloseDocumentWithDelegate:(id)delegate shouldCloseSelector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo
{
   if ( [[MyAppController instance] windowShouldClose: delegate] )
      if ( [delegate respondsToSelector:@selector(close)] )
         [delegate close];
   //[delegate performSelector:shouldCloseSelector];
   //if ( [delegate isKindOfClass:[NSWindow class]] )
   //   [delegate performClose:self]; // :self];
   return; // handled by [[MyAppController instance] windowShouldClose:(id)sender
}

- (void)shouldCloseWindowController:(NSWindowController *)windowController delegate:(id)delegate shouldCloseSelector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo
{
   if ( [[MyAppController instance] windowShouldClose: [windowController window]] )
      if ( [[windowController window] respondsToSelector:@selector(close)] )
         [[windowController window] close];
}

- (void) beginUndoGroup: (NSNotification *)iNotification
{
   NSUndoManager *undoMgr = [self undoManager];
   if ( [undoMgr groupingLevel] == 1 )
   {
      // do your custom stuff here
   }
}

// convenience functions:
- (NSString *)filePath { return [[self fileURL] path]; }
- (void) setFilePath: (NSString *)filePath 
{ 
   if ( [filePath length] )
      [self setFileURL:[NSURL fileURLWithPath: filePath]];
   else
      [self setFileURL:nil];
}

- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
{
   if ( [self isDocumentEdited] && [anItem action] == @selector(revertDocumentToSaved:) )
      return YES;
   BOOL retVal = [super validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem];
   return retVal;
}

- (IBAction)revertDocumentToSaved:(id)sender
{
   NSInteger retVal = NSRunAlertPanel(@"Revert To Saved?", [NSString stringWithFormat: @"Revert to Saved File %@?", [self filePath]], @"Revert to Saved", @"Cancel", NULL);
   if ( retVal == NSAlertDefaultReturn )
      [[MyAppController instance] myOpenFile:[self filePath]];
}

+ (BOOL) openMyDocument: (NSString *)filename
{
   if ( ![[filename pathExtension] isEqualToString: kMyAppConsoleFileExtension] )
      return NO;

   // If the user started up the application by double-clicking a file, the delegate receives the application:openFile: message FIRST
   MyLaunchStatus launchStatus = [[MyAppController instance] isFinishedLaunching];
   BOOL userDoubleClickedToLaunchApp = !( launchStatus & kFinishedPreLaunchCheck );

   MyDocument *currDoc = [[MyAppController instance] document];
   NSString *currPath = [currDoc filePath];
   NSInteger retVal;
   NSLog( @"open file %@ currPath %@ launchStatus %d", filename, currPath, launchStatus );
   if ( userDoubleClickedFileToLaunchApp )
   {
      // user double-clicked a file to start MyApp
      currPath = [[NSUserDefaults standardUserDefaults] objectForKey:@"LastSaveFile"];
      if ( [currPath isEqualToString: filename] )
      {
         sWasAlreadyOpen = YES;
         if ( [[[NSUserDefaults standardUserDefaults] objectForKey:@"isDocumentEdited"] boolValue] == YES )
         {
            retVal = NSRunAlertPanel(@"Open File", @"Revert to Saved?", @"Revert to Saved", @"Keep Changes", @"Quit", NULL);
            if ( retVal == NSAlertDefaultReturn )
            {
               [[MyAppController instance] myOpenFile:filename];
            }
            else if ( retVal == NSAlertOtherReturn )
               exit(0);
         }

         // proceed with normal startup
         if ( currDoc )
            return YES;
         else
            return NO;
      }
   }

   if ( !(launchStatus & kFinishedPreLaunchCheck ) ) // not done launching
      return YES; // startup in whatever state we were before

   if ( [currPath isEqualToString: filename] )
   {
      sWasAlreadyOpen = YES;
      NSLog( @"is edited %d currDoc %@", [currDoc isDocumentEdited], currDoc );
      if ( [currDoc isDocumentEdited] )
         [currDoc revertDocumentToSaved:self]; // will prompt
      else // document is already open, so do what Apple's standard action is... 
         [currDoc showWindows];
   }
   else 
   {
      if ( [currDoc isDocumentEdited] )
         retVal = NSRunAlertPanel(@"Open File", [NSString stringWithFormat: @"The current file has unsaved changes.  Discard unsaved changes and switch to file '%@'?", filename], @"Discard unsaved changes and switch to file", @"Keep Current", NULL);
      else
         retVal = NSRunAlertPanel(@"Switch to File", [NSString stringWithFormat: @"Switch to File '%@'?\n\nCurrent file '%@'", filename, currfilePath ? currfilePath : @"Untitled"], @"Switch", @"Keep Current", NULL);
      if ( retVal == NSAlertDefaultReturn )
         [[MyAppController instance] myOpenFile:filename];
   }

   // user cancelled
   if ( currDoc )
      return YES;
   else
      return NO;
}


// Note: readFromURL is here for completeness, but it should never be called,
// because we override NSDocumentController documentForURL below.
- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
{
   if ( outError )
      *outError = nil;
   if ( ![typeName isEqualToString: kMyFileTypeExtension ] ) // 
      return NO;
   return YES;
}

// Note: writeToURL is here for completeness, but it should never be called,
// because we override NSDocumentController documentForURL below.
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
{
   if ( outError )
      *outError = nil;
   return YES;
}
@end


// kpk migrating slowly toward NSDocument framework 
// (currently most functionality is in MyAppController)
// Must bypass default NSDocumentController behavior to allow only 1 document
// and keep MyAppController responsible for read, write, dialogs, etc.
@implementation MyDocumentController

// this should be the only override needed to bypass NSDocument dialogs, readFromURL,
// and writeToURL calls.
// Note: To keep Lion happy, MainInfo.plist and Info.plist must define "MyDocument" for key "NSDocumentClass"
- (id)documentForURL:(NSURL *)absoluteURL
{
   MyDocument *currDoc = [[MyAppController instance] document];
   if ( [[currDoc filePath] isEqualToString: [absoluteURL path]] )
      return currDoc;
   else
      return nil;
}

@end

#pragma mark -
@implementation MyAppController
static MyAppController *sInstance;

+ (MyAppController *)instance
{
   return sInstance; // singleton... why is this not in all Apple's sample code?
}


// called by main.mm before MyAppController (or NSApp for that matter) is created.
// need to init some global variables here.
+ (void) beforeAwakeFromNib
{
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   ... implementation specific ...

   // disable fancy stuff that slows launch down
   [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey: @"NSAutomaticWindowAnimationsEnabled"];
   [pool release];
}


- (void) awakeFromNib
{
   NSLog(@"MyAppController awake\n");
   sInstance = self;

   [toWindow setNextResponder:self];
   [NSApp setDelegate:self];


   [self addWindowToDocument:toWindow];
}

- (MyDocument *)document 
{ 
   if ( !toDocument )
   {
      toDocument = [[MyDocument alloc] init];
   }
   return toDocument; 
}

- (NSUndoManager *)undoManager
{
   // !!! WARNING: there are multiple NSUndoManager's in this App

   // Note: when an editable text field is in focus, 
   // NSTextField will create 
   // a separate undo manager for editing text while that field is in focus.
   // This means that hitting undo/redo while editing a text field will not go beyond the scope of that field.

   // This will return the global undo manager if the keyWindow was registered
   // via [self addWindowToDocument:];
   // Windows which are NOT part of the document (such as preferences, popups, etc.), will
   // return their own undoManager, and undo will do nothing while those windows are in front.
   // You can't undo preferences window changes, so we don't want to surprise the user.
   NSUndoManager *undomgr =  [[NSApp keyWindow] undoManager];
   if ( undomgr )
   {
      static bool sFirstTime = true;
      if ( sFirstTime )
      {
         sFirstTime = false;
         [undomgr setLevelsOfUndo:1000]; // set some sane limit
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(beginUndo:) 
                                                      name:NSUndoManagerWillUndoChangeNotification 
                                                    object:nil]; 
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(beginUndo:) 
                                                      name:NSUndoManagerWillRedoChangeNotification 
                                                    object:nil];  

         [toDocument registerForUndoGrouping];
//         [[NSNotificationCenter defaultCenter] addObserver:self
//                                                  selector:@selector(endUndo:) 
//                                                      name:NSUndoManagerDidUndoChangeNotification 
//                                                    object:nil];       
//         [[NSNotificationCenter defaultCenter] addObserver:self
//                                                  selector:@selector(endUndo:) 
//                                                      name:NSUndoManagerDidRedoChangeNotification 
//                                                    object:nil];  
      }

   }
   return undomgr;
}

- (void) showStatusText: (id)iStatusText
{
  ... implementation specific ...
}

- (void) beginUndo:(id)sender
{
  // implementation specific stuff here
     NSUndoManager *undomgr =  [[NSApp keyWindow] undoManager];

   if ( [sender object] == undomgr )
   {
      if ( [undomgr isUndoing] )
         [self showStatusText: [NSString stringWithFormat:@"Undo %@", [undomgr undoActionName]]];
      else if ( [undomgr isRedoing] )
         [self showStatusText: [NSString stringWithFormat:@"Redo %@", [undomgr redoActionName]]];
   }
}

// Add a window (with a window controller) to our document, so that the window
// uses the document's NSUndoManager.  In the future, we may want to use other features of NSDocument.
- (void)addWindowToDocument:(NSWindow *)iWindow
{
   NSString *autosaveName = [iWindow frameAutosaveName]; // preserve for "mainWindow", others.
   NSWindowController *winController = [iWindow windowController];
   if ( !winController )
      winController = [[NSWindowController alloc] initWithWindow:iWindow];

   // create document if needed, and add window to document.
   [[self document] addWindowController: winController];
   if ( autosaveName )
      [iWindow setFrameAutosaveName:autosaveName]; // restore original for "mainWindow", others.
   [winController setNextResponder:self]; // keep last hotkey destination... see keyDown:

}

- (void) myOpenFile:(NSString*)path
{
  // this is just a skeleton of what I do to track unsaved changes between relaunches

   [toDocument setFilePath:path];

  ... implementation specific ...
      [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isDocumentEdited"];
      [toDocument updateChangeCount:NSChangeCleared];
      [[NSUserDefaults standardUserDefaults] setObject:[toDocument filePath] forKey:@"LastSaveFile"];

      BOOL success = [[NSUserDefaults standardUserDefaults] synchronize]; // bootstrap
      // kpk very important... resetStandardUserDefaults forces the immutable
      // tree returned by dictionaryWithContentsOfFile to be mutable once re-read.
      // Apple: "Synchronizes any changes made to the shared user defaults object and releases it from memory.
      //         A subsequent invocation of standardUserDefaults creates a new shared user defaults object with the standard search list."
      [NSUserDefaults resetStandardUserDefaults];

      NSString *name = [[NSUserDefaults standardUserDefaults] objectForKey:@"LastSaveFile"]; 
}

- (void) mySaveData:(NSString*)path
{
  // this is just a skeleton of what I do to track unsaved changes between relaunches
     @try 
   {
  ... implement something inherently dangerous... commit changes to a file ...
         if ( !errorStr )
         {
            if ( [toDocument isDocumentEdited] )
            {
               // UInt64 theTimeNow = VMPGlue::GetMilliS();
               [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isDocumentEdited"];
               [[NSUserDefaults standardUserDefaults] synchronize]; // bootstrap
               // DLog( @"synchronize success %d ms", (int)(VMPGlue::GetMilliS() - theTimeNow) );
            }
            // tbd consider MyDocument saveToURL:ofType:forSaveOperation:error:
            [toDocument updateChangeCount:NSChangeCleared];
         }
   @catch (...)
   {
      ... run critical alert ...
   }
}

- (void) finishLoadingData
{
   @try 
   {


     if ( dataexists )
     {
          ... implement something inherently dangerous... loading your data from a file


      [toDocument setFilePath: [[NSUserDefaults standardUserDefaults] objectForKey:@"LastSaveFile"]];
      NSNumber *num = [[NSUserDefaults standardUserDefaults] objectForKey:@"isDocumentEdited"];
      if ( [num boolValue] == YES )
         [toDocument updateChangeCount:NSChangeDone];
     }
     else
     {
        [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isDocumentEdited"];
        [toDocument updateChangeCount:NSChangeCleared];
     }

     sFinishedLaunching |= kFinishedLoadingData;
   }
   @catch (...)
   {
      // !!! will not return !!!
      ... run critical alert ...
      // !!! will not return !!!
   }
}
#pragma mark NSApplication delegate

// Apple: Sent directly by theApplication to the delegate. The method should open the file filename, 
//returning YES if the file is successfully opened, and NO otherwise. 
//If the user started up the application by double-clicking a file, the delegate receives the application:openFile: message before receiving applicationDidFinishLaunching:. 
//(applicationWillFinishLaunching: is sent before application:openFile:.)
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
{
   BOOL didOpen = [MyDocument openMyDocument: filename];
   sFinishedLaunching |= kFinishedOpenFile;
   return didOpen;
}

// NSApplication notification
- (void) applicationDidFinishLaunching:(NSNotification*)note
{
   // kpk note: currentEvent is often nil at this point! [[NSApp currentEvent] modifierFlags]
   CGEventFlags modifierFlags = CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState);

   sFinishedLaunching |= kFinishedWaking;
   if ( modifierFlags & (kCGEventFlagMaskShift | kCGEventFlagMaskCommand) )
   {
      ... implementation specific ... alert: @"Shift or Command key held down at startup.\nWhat would you like to do?" 
                           title: @"Startup Options"
                     canContinue: @"Continue" ];
   }
   sFinishedLaunching |= kFinishedPreLaunchCheck;
   [self finishLoadingData];
   sFinishedLaunching |= kFinishedAndReadyToDraw;   
}

- (BOOL)windowShouldClose:(id)sender
{
   if ( [sender isKindOfClass: [NSWindow class]] && sender != toWindow )
      return YES; // allow non-document-edit windows to close normally

   ... in my implementation, I prompt user with save/quit options when edit window is closed ...
   return NO;
}

- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender
{
   if ( !gWantsToQuit && [toDocument isDocumentEdited] )
   {
      if ( ![self windowShouldClose:self] )
         return NSTerminateCancel;

   }
   return NSTerminateNow;
}

- (void) applicationWillTerminate:(NSNotification *)notification
{
   if ( gWantsToQuit )
   {
      ... implementation specific ... dont save potentially wonky data if relaunch is required
   }
   else
   {  
      [self saveData: [toDocument filePath]];
   }
}
@end
@implementation AppDelegate

- (id)init
{
    self = [super init];
    if (self) {
        MyDocumentController *dc = [[MyDocumentController alloc] init];
        if (dc) {};
    }
    return self;
}
// In MyDocumentController.h
@interface MyDocumentController : NSDocumentController
@end

// In MyDocumentController.m
@implementation MyDocumentController
  // ... your custom code here
@end

// In MyAppDelegate.h
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (nonatomic, strong) IBOutlet MyDocumentController *myController;
@end
// In MyAppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSLog(@"sharedController is %@", [NSDocumentController sharedController]);
}
<MyDocumentController: 0x600000044710>
@implementation AppDocumentController

+ (void) load
{
    [AppDocumentController new];
}

// ... your overriding goes here

@end
class AppDelegate: NSObject {
    let docController = DocController()
}

class DocController: NSDocumentController {
}