Domanda

Normalmente un bundle su OS X può essere avviato solo una volta, tuttavia, semplicemente copiando il bundle la stessa applicazione può essere lanciato due volte.Qual è la strategia migliore per rilevare e fermare questa possibilità?

In Windows, questo effetto può semplicemente essere ottenuto mediante l'applicazione di creazione di una risorsa chiamata al lancio e poi esci se il nome della risorsa non può essere creata, che indica che un altro processo è in esecuzione e che ha già creato la stessa risorsa.Queste risorse sono rilasciati in un modo affidabile di Windows quando si chiude l'applicazione.

Il problema che ho visto, quando la ricerca di questo è che le Api di OS X mantenere stato nel file di sistema e quindi rende la strategia utilizzata su windows inaffidabile, io.e persistente file dopo un uscita improprio può indicare erroneamente che l'applicazione è già in esecuzione.

Le Api che posso usare per ottenere lo stesso effetto su OS X sono:posix, di carbonio e di spinta.

Idee?

È stato utile?

Soluzione

Un basso livello di soluzione è usare il gregge().

Ogni istanza di tentativo di bloccare un file all'avvio, e se il blocco non riesce poi un altro esempio è già in esecuzione.Greggi sono automaticamente rilasciato quando il programma si chiude, quindi nessuna preoccupazione per raffermo serrature.

Si noti che qualsiasi soluzione si scelga, è necessario fare una decisione consapevole su cosa significa avere "istanze".In particolare, se più utenti sono in esecuzione la vostra app, allo stesso tempo, è ok?

Altri suggerimenti

Questo è estremamente facile in Snow Leopard:

- (void)deduplicateRunningInstances {
    if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]] count] > 1) {
        [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]] 
                         defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal];

        [NSApp terminate:nil];
    }
}

Vedere http://blog.jseibert.com/post/1167439217/deduplicating-running-instances-or-how-to-detect-if per ulteriori informazioni.

C'è un misterioso Info.plist chiave denominata "Applicazione vieta di più istanze," ma non sembra funzionare per me.Sto scrivendo un CLI applicazione e l'esecuzione all'interno di un bundle.Forse sarebbe lavorare in una applicazione GUI, ma non ho provato.

Come è già stato detto Cacao applicazioni di solito non consentono di eseguire più di una istanza alla volta.

In generale, cacao modo per risolvere questo launchedApplications in NSWorkspace.Questo restituisce un NSArray contenente un dizionario per ogni applicazione avviata.È possibile scorrere l'array per vedere se l'applicazione che si sta cercando è già in esecuzione.Vorrei consigliare che si utilizza il valore con il tasto NSApplicationBundleIdentifier che avrà un valore come "com.mycompany.myapp" piuttosto che cercando il nome.Se hai bisogno di trovare l'identificatore pacchetto di un'applicazione è possibile guardare le sue informazioni.file plist nel pacchetto dell'app.

Prima di tutto, è “Mac OS X” o “OS X”.Non c'è nessuna tale cosa come “OS/X”.

Secondo, Mac OS X non venire con Boost;si avrebbe bisogno di esso bundle con l'applicazione.

Terzo, la maggior parte del Carbonio non è disponibile nelle versioni a 64-bit.Questo è un chiaro segnale che le porzioni di Carbonio andrà via un giorno (quando Apple abbandona a 32-bit in hardware).Prima o poi, sarà necessario riscrivere le app con Cacao o abbandonare il Mac.

Normalmente un bundle su OS/X può essere avviato solo una volta, tuttavia è sufficiente rinominare il bundle la stessa applicazione può essere lanciato due volte.

No, non è possibile.Il lancio rinominato o spostato applicazione sarà sufficiente attivare (portare a fronte del processo che era già in esecuzione;non si avvia un nuovo processo accanto al primo.


Ci sono diversi modi per dire se un'applicazione è già in esecuzione.In ogni caso, non vi è questo il lancio:

  1. L'uso del Cacao connessione nsconnection registrare una connessione con un singolo nome di costante.Questo avrà esito negativo se il nome è già registrato.(Si può usare Fondazione da parte di un Carbonio app;e ' l'Applicazione del Kit è necessario essere cauti con.)
  2. Utilizzare il Gestore del Processo di scansione il processo di elenco per i processi il cui identificatore pacchetto corrisponde a quello che stai cercando.Il bundle identifier non è immutabile, ma è più difficile di cambiare il nome del file o il percorso.
  3. Se stai cercando di vedere quando qualcuno esegue una seconda copia di te stesso, è possibile utilizzare CFNotificationCenter:

    1. Aggiungi a te stesso come un osservatore per “com.yourdomain.nomeapp.LaunchResponse”.
    2. Pubblicare sotto il nome di “com.yourdomain.nomeapp.LaunchCall”.
    3. Aggiungi a te stesso come un osservatore per “com.yourdomain.nomeapp.LaunchCall”.

    Nella tua osservazione di callback per la notifica di Chiamata, posta la notifica di Risposta.
    Nella tua osservazione di callback per la notifica di Risposta, uscita.

    Così, quando il primo processo inizia, e non ottenere alcuna Risposta;quando il secondo processo inizia, Chiama, riceve una Risposta dal primo processo, e l'uscita in ossequio alla prima.

Questa è una combinazione di Romani e di Jeff risposte per Swift 2.0:Se un altro esempio di app con lo stesso bundle ID è già in esecuzione, mostra un alert, attivare l'altra istanza e chiudere il duplicato istanza.

func applicationDidFinishLaunching(aNotification: NSNotification) {
    /* Check if another instance of this app is running. */
    let bundleID = NSBundle.mainBundle().bundleIdentifier!
    if NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleID).count > 1 {
        /* Show alert. */
        let alert = NSAlert()
        alert.addButtonWithTitle("OK")
        let appName = NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleNameKey as String) as! String
        alert.messageText = "Another copy of \(appName) is already running."
        alert.informativeText = "This copy will now quit."
        alert.alertStyle = NSAlertStyle.CriticalAlertStyle
        alert.runModal()

        /* Activate the other instance and terminate this instance. */
        let apps = NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleID)
        for app in apps {
            if app != NSRunningApplication.currentApplication() {
                app.activateWithOptions([.ActivateAllWindows, .ActivateIgnoringOtherApps])
                break
            }
        }
        NSApp.terminate(nil)
    }

    /* ... */
}

Che dire IPC?Si potrebbe aprire un socket e negoziare con le altre lanciato istanza.È necessario essere attenti, però, che funziona, ma solo se entrambe le applicazioni allo stesso tempo.

Io non posso fornire codice di esempio, come non ho (ancora, ma lo farò presto) usata.

Questa è una versione di seb per Swift 3.0:Se un altro esempio di app con lo stesso bundle ID è già in esecuzione, mostra un alert, attivare l'altra istanza e chiudere il duplicato istanza.

func applicationDidFinishLaunching(aNotification: NSNotification) {
    /* Check if another instance of this app is running. */
    let bundleID = Bundle.main.bundleIdentifier!
    if NSRunningApplication.runningApplications(withBundleIdentifier: bundleID).count > 1 {
         /* Show alert. */
         let alert = NSAlert()
         alert.addButton(withTitle: "OK")
         let appName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String
         alert.messageText = "Another copy of \(appName) is already running."
         alert.informativeText = "This copy will now quit."
         alert.alertStyle = NSAlert.Style.critical
         alert.runModal()

         /* Activate the other instance and terminate this instance. */
         let apps = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID)
             for app in apps {
                  if app != NSRunningApplication.current {
                      app.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
                      break
                  }
             }
                NSApp.terminate(nil)
         }   
       /* ... */
}

di rilevare se l'applicazione con lo stesso codice bundleid è in esecuzione, attivare e chiudere quello che inizia.

- (id)init method of < NSApplicationDelegate >

    NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
    if ([apps count] > 1)
    {
        NSRunningApplication *curApp = [NSRunningApplication currentApplication];
        for (NSRunningApplication *app in apps)
        {
            if(app != curApp)
            {
                [app activateWithOptions:NSApplicationActivateAllWindows|NSApplicationActivateIgnoringOtherApps];
                break;
            }
        }
        [NSApp terminate:nil];
        return nil;
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top