Articles

XPC szolgáltatások a macOS alkalmazáson

Lê Điền Phúc
Lê Điền Phúc

Follow

Szept. 4, 2020 – 9 min read

Az XPC előtt Socketeket és Mach üzeneteket (Mach Ports) vettünk fel.

Az XPC mechanizmus alternatívát kínál a socketek (vagy a MIG-et használó Mach Services) helyett az IPC számára. Lehet például egy olyan folyamatunk, amely “kiszolgálóként” várja, hogy a kliensek hozzáférjenek az API-jához, és valamilyen szolgáltatást nyújtsanak.

XPC Services on applications

Amikor XPC Services-ről (nagy ‘S’) beszélünk, akkor az XPC Service nevű csomagra utalunk. A csomagok az Apple ökoszisztémájában egy adott könyvtárszerkezet által reprezentált entitásokra utalnak. A leggyakoribb Bundle, amivel találkozhatunk, az Application Bundle. Ha jobb egérgombbal kattintasz bármelyik alkalmazásra (például a Chess.app-ra), és kiválasztod a Tartalom megjelenítése lehetőséget, akkor egy könyvtárszerkezetet találsz. Visszatérve az XPC-hez, az alkalmazásoknak lehetnek XPC Service csomagjai. Ezeket a Contents/XPCServices/ könyvtárban találja az alkalmazáscsomagon belül. Yo rákereshet a /Applications könyvtárában, és megnézheti, hogy hány alkalmazás támaszkodik az XPC szolgáltatásokra.

A XPC szolgáltatások a keretrendszereken belül is lehetnek (amelyek egy másik típusú csomagok).

Az XPC szolgáltatások további előnyei

Az XPC szolgáltatások használata az alkalmazásainkban lehetővé teszi számunkra, hogy bizonyos funkciókat külön modulokba (Az XPC szolgáltatás) bontsunk. Létrehozhatunk egy XPC Service-t, amely néhány költséges, de ritkán előforduló feladat futtatásáért felelhet. Például valamilyen kriptográfiai feladatot a véletlen számok generálására.

Egy másik további előnye, hogy az XPC Service saját folyamaton fut. Ha ez a folyamat összeomlik vagy megölik, az nem érinti a fő alkalmazásunkat. Képzeljük el, hogy az alkalmazásunk támogatja a felhasználó által definiált bővítményeket. A bővítményeket pedig az XPC Services segítségével építjük fel. Ha rosszul vannak kódolva és összeomlanak, nem befolyásolják a fő alkalmazás integritását.

Az XPC Service további előnye, hogy saját jogosultságokkal rendelkezhetnek. Az alkalmazásnak csak akkor lesz szüksége a jogosultságra, ha olyan XPC Service által nyújtott szolgáltatást vesz igénybe, amelyhez a jogosultság szükséges. Képzeljük el, hogy van egy alkalmazásunk, amely a helymeghatározást használja, de csak bizonyos funkciókhoz. Ezeket a funkciókat áthelyezheti egy XPC szolgáltatásba, és a helymeghatározási jogosultságot csak ehhez az XPC szolgáltatáshoz adhatja hozzá. Ha a felhasználónak soha nincs szüksége a helymeghatározást használó funkcióra, akkor nem fog kérni jogosultságot, így az alkalmazás használata megbízhatóbbá válik.

XPC és barátunk, a launchd

A launchd az első folyamat, amely lefut a rendszerünkön. Feladata más folyamatok, szolgáltatások és daemonok indítása és kezelése. launchd felelős a feladatok ütemezéséért is. Így logikus, hogy a launchd lesz felelős az XPC szolgáltatások kezeléséért is.

Az XPC szolgáltatás leállítható, ha hosszú ideig tétlen volt, vagy igény szerint létrehozható. Az összes kezelést a launchd végzi, és nekünk nem kell semmit sem tennünk ahhoz, hogy működjön.

a launchd rendelkezik információkkal a rendszerszintű erőforrások rendelkezésre állásáról és a memóriaterhelésről, ki tud jobban dönteni arról, hogyan használjuk a leghatékonyabban a rendszerünk erőforrásait, mint a launchd

XPC szolgáltatások megvalósítása

Az XPC szolgáltatás egy köteg a fő alkalmazáscsomag Contents/XPCServices könyvtárában; az XPC szolgáltatásköteg tartalmaz egy Info.plist fájlt, egy futtatható fájlt és a szolgáltatáshoz szükséges erőforrásokat. Az XPC szolgáltatás az xpc_main(3) Mac OS X Developer Tools Manual Page meghívásával jelzi, hogy melyik függvényt hívja meg, amikor a szolgáltatás üzeneteket kap, a főfüggvényéből.

Az XPC szolgáltatás létrehozásához az Xcode-ban a következőket kell tennie:

  1. Adjon hozzá egy új célt a projektjéhez, az XPC szolgáltatás sablon használatával.
  2. Adjon hozzá egy Fájlok másolása fázist az alkalmazás építési beállításaihoz, amely az XPC szolgáltatást a fő alkalmazáscsomag Contents/XPCServices könyvtárába másolja.
  3. Adjon hozzá egy függőséget az alkalmazás építési beállításaihoz, hogy jelezze, hogy az XPC szolgáltatáscsomagtól függ.
  4. Ha alacsony szintű (C-alapú) XPC szolgáltatást ír, implementáljon egy minimális főfüggvényt az eseménykezelő regisztrálásához, ahogy az a következő kódlistában látható. Helyettesítse a my_event_handler-t az eseménykezelő függvény nevével.
int main(int argc, const char *argv) {
xpc_main(my_event_handler);
// The xpc_main() function never returns.
exit(EXIT_FAILURE);
}

Ha magas szintű (Objective-C-alapú) szolgáltatást ír az NSXPCConnection használatával, először hozzon létre egy, az NSXPCListenerDelegate protokollnak megfelelő kapcsolati delegált osztályt. Ezután implementáljon egy minimális főfüggvényt, amely létrehoz és konfigurál egy listener objektumot, ahogy az a következő kódlistában látható.

int main(int argc, const char *argv) {
MyDelegateClass *myDelegate = ...
NSXPCListener *listener =
;
listener.delegate = myDelegate;
;
// The resume method never returns.
exit(EXIT_FAILURE);
}

A szolgáltatás használata

Az XPC szolgáltatás használatának módja attól függ, hogy a C API-val (XPC Services) vagy az Objective-C API-val (NSXPCConnection) dolgozik.

Az Objective-C NSXPCConnection API használata Az Objective-C NSXPCConnection API egy magas szintű távoli eljáráshívó interfészt biztosít, amely lehetővé teszi, hogy egy másik folyamatból (általában egy XPC szolgáltatás egy metódusát hívó alkalmazásból) egy folyamat objektumainak metódusait hívja meg. Az NSXPCConnection API automatikusan szerializálja az adatstruktúrákat és objektumokat az átvitelhez, és deszerializálja azokat a másik oldalon. Ennek eredményeképpen egy távoli objektumon egy metódus hívása ugyanúgy viselkedik, mint egy helyi objektumon.

Az NSXPCConnection API használatához a következőket kell létrehoznia:

  • Egy interfészt. Ez főként egy protokollból áll, amely leírja, hogy milyen metódusok legyenek hívhatók a távoli folyamatból. Ezt a következő fejezetben ismertetjük: Egy interfész tervezése
  • Egy kapcsolati objektum mindkét oldalon. A szolgáltatás oldalán ezt korábban a Szolgáltatás létrehozása című fejezetben ismertettük. Az ügyféloldalon ezt a Csatlakozás egy interfészhez és használata című fejezetben ismertettük.
  • Egy figyelő. Ez a kód az XPC szolgáltatásban fogadja a kapcsolatokat. Ezt a Kapcsolat elfogadása a segédprogramban című fejezetben ismertetjük. Üzenetek.

Általános architektúra

Általános architektúra

Az NSXPCCconnection-alapú segédalkalmazásokkal való munka során mind a fő alkalmazás, mind a segédalkalmazás rendelkezik az NSXPCConnection egy példányával. A főalkalmazás maga hozza létre a kapcsolati objektumát, ami a segédalkalmazás elindítását okozza. A segédalkalmazás delegált metódusa a kapcsolat létrehozásakor megkapja a kapcsolati objektumát. Ezt szemlélteti a 4-1. ábra.

Minden NSXPCConnection objektum három fő jellemzőt biztosít:

  • Egy exportedInterface tulajdonság, amely leírja azokat a metódusokat, amelyeket a kapcsolat másik oldala számára elérhetővé kell tenni.
  • Egy exportedObject tulajdonság, amely egy helyi objektumot tartalmaz a kapcsolat másik oldaláról érkező metódushívások kezelésére.
  • A kapcsolat másik oldalán lévő metódusok hívásához proxy objektum megszerzésének lehetősége.

Amikor a főalkalmazás meghív egy metódust egy proxyobjektumon, az XPC szolgáltatás NSXPCConnection objektuma az exportedObject tulajdonságában tárolt objektumon hívja meg az adott metódust.

Hasonlóképpen, ha az XPC szolgáltatás megszerez egy proxyobjektumot, és meghív egy metódust ezen az objektumon, a fő alkalmazás NSXPCConnection objektuma meghívja ezt a metódust az exportedObject tulajdonságában tárolt objektumon

A felület tervezése

Az NSXPCConnection API az Objective-C protokollokat használja ki a hívó alkalmazás és a szolgáltatás közötti programozási felület meghatározásához. Minden olyan példánymetódust, amelyet a kapcsolat másik oldaláról meg akar hívni, kifejezetten meg kell határozni egy formális protokollban. Például:

@protocol FeedMeACookie
- (void)feedMeACookie: (Cookie *)cookie;
@end

Mivel az XPC-n keresztüli kommunikáció aszinkron, a protokollban szereplő összes metódus visszatérési típusa void kell, hogy legyen. Ha adatot kell visszaadnunk, akkor egy válaszblokkot így definiálhatunk:

@protocol FeedMeAWatermelon
- (void)feedMeAWatermelon: (Watermelon *)watermelon
reply:(void (^)(Rind *))reply;
@end

Egy metódusnak csak egy válaszblokkja lehet. Mivel azonban a kapcsolatok kétirányúak, az XPC szolgáltatás-segédprogram szükség esetén a főalkalmazás által biztosított interfész metódusainak meghívásával is válaszolhat.

Minden metódus visszatérési típusának void-nak kell lennie, és a metódusok vagy válaszblokkok minden paraméterének vagy:

  • Aritmetikai típusok (int, char, float, double, uint64_t, NSUInteger stb.)
  • BOOL
  • C karakterláncok
  • C struktúrák és tömbök, amelyek csak a fent felsorolt típusokat tartalmazzák
  • Az NSSecureCoding protokollt megvalósító objektumok.

Fontos: Ha egy metódusnak (vagy annak válaszblokkjának) olyan paraméterei vannak, amelyek Objective-C gyűjtőosztályok (NSDictionary, NSArray stb.), és ha egy gyűjteményen belül saját egyéni objektumokat kell átadni, akkor kifejezetten meg kell mondania az XPC-nek, hogy engedélyezze az adott osztályt a gyűjtőparaméter tagjaként.

Kapcsolódás egy interfészhez és használata

Amikor definiálta a protokollt, létre kell hoznia egy interfészobjektumot, amely leírja azt. Ehhez hívja meg az NSXPCInterface osztály interfaceWithProtocol: metódusát. Például:

NSXPCInterface *myCookieInterface =
;

Mihelyt létrehozta az interfészobjektumot, a fő alkalmazáson belül konfigurálnia kell vele a kapcsolatot az initWithServiceName: metódus meghívásával. Például:

NSXPCConnection *myConnection = 
initWithServiceName:@"com.example.monster"];
myConnection.remoteObjectInterface = myCookieInterface;
;

Megjegyzés: Az alkalmazáscsomagon kívüli XPC szolgáltatásokkal való kommunikációhoz az initWithMachServiceName: metódussal is konfigurálhat XPC kapcsolatot.

A fő alkalmazás ekkor meghívhatja a myConnection objektum remoteObjectProxy vagy remoteObjectProxyWithErrorHandler: metódusát, hogy egy proxy objektumot kapjon.

Ez az objektum annak az objektumnak a proxyjaként működik, amelyet az XPC szolgáltatás exportált objektumként állított be (az exportedObject tulajdonság beállításával). Ennek az objektumnak meg kell felelnie a remoteObjectInterface tulajdonság által meghatározott protokollnak.

Amikor az alkalmazás meghív egy metódust a proxy-objektumon, a megfelelő metódus az XPC szolgáltatáson belül az exportált objektumon hívódik meg. Amikor a szolgáltatás metódusa meghívja a válaszblokkot, a paraméterértékek szerializálásra kerülnek, és visszaküldésre kerülnek az alkalmazáshoz, ahol a paraméterértékek deserializálásra kerülnek, és átadásra kerülnek a válaszblokknak. (A válaszblokk az alkalmazás címtartományán belül hajtódik végre.)

Megjegyzés: Ha lehetővé kívánja tenni, hogy a segédfolyamat az alkalmazásban lévő objektumon metódusokat hívjon meg, akkor a folytatás hívása előtt be kell állítania az exportedInterface és az exportedObject tulajdonságokat. Ezek a tulajdonságok a következő szakaszban kerülnek ismertetésre.

Kapcsolat fogadása a segédfolyamatban

Amikor egy NSXPCConnection-alapú segédfolyamat megkapja az első üzenetet egy kapcsolattól, a hallgató delegátus listener:shouldAcceptNewConnection: metódusát egy hallgatóobjektummal és egy kapcsolati objektummal hívja meg. Ez a metódus lehetővé teszi annak eldöntését, hogy elfogadja-e a kapcsolatot vagy sem; a kapcsolat elfogadásához YES-t, a kapcsolat elutasításához pedig NO-t kell visszaadnia.

Megjegyzés: A segítő akkor kapja a kapcsolatkérést, amikor az első tényleges üzenet elküldésre kerül. A kapcsolatobjektum resume metódusa nem okoz üzenetküldést.

Az irányelvi döntések meghozatalán kívül ennek a metódusnak konfigurálnia kell a kapcsolatobjektumot. Különösen, ha a segítő úgy dönt, hogy elfogadja a kapcsolatot, akkor a következő tulajdonságokat kell beállítania a kapcsolaton:

  • exportedInterface – egy interfészobjektum, amely leírja az exportálni kívánt objektum protokollját. (Ennek az objektumnak a létrehozását korábban a Kapcsolódás egy interfészhez és használata című fejezetben ismertettük.)
  • exportedObject – az a helyi objektum (általában a segédprogramban), amelynek a távoli kliens metódushívásait át kell adni. Amikor a kapcsolat másik vége (általában az alkalmazásban) meghív egy metódust a kapcsolat proxy objektumán, a megfelelő metódust az exportedObject tulajdonság által megadott objektumon hívja meg.

Az említett tulajdonságok beállítása után az IGEN visszatérése előtt meg kell hívnia a kapcsolat objektumának resume metódusát. Bár a delegált elhalaszthatja a resume meghívását, a kapcsolat addig nem fog üzeneteket fogadni, amíg ezt nem teszi meg.

Üzenetek küldése

Az NSXPC-vel történő üzenetküldés olyan egyszerű, mint egy metódushívás. Például a myCookieInterface (korábbi szakaszokban ismertetett) interfész birtokában az XPC kapcsolat myConnection objektumán a feedMeACookie metódust a következőképpen hívhatjuk meg:

Cookie *myCookie = ...
feedMeACookie: myCookie];

A metódus meghívásakor az XPC-segédprogram megfelelő metódusa automatikusan meghívásra kerül. Ez a metódus viszont hasonló módon használhatja az XPC-segítő kapcsolati objektumát, hogy meghívjon egy metódust a főalkalmazás által exportált objektumon.

Hibakezelés

A segítő adott feladatára jellemző hibakezelési metódusokon kívül mind az XPC-szolgáltatásnak, mind a főalkalmazásnak a következő XPC hibakezelő blokkokat kell biztosítania:

  • Megszakításkezelő – akkor hívható, ha a kapcsolat másik végén lévő folyamat leállt vagy más módon lezárta a kapcsolatát. A helyi kapcsolati objektum jellemzően továbbra is érvényes – minden jövőbeli hívás automatikusan új segédpéldányt hoz létre, kivéve, ha ez nem lehetséges -, de előfordulhat, hogy vissza kell állítani minden olyan állapotot, amelyet a segéd egyébként megtartott volna.

A kezelő ugyanabban a sorban hívódik meg, mint a válaszüzenetek és más kezelők, és mindig minden más üzenet vagy válaszblokk kezelő után hajtódik végre (kivéve az érvénytelenítési kezelőt). A megszakításkezelőből biztonságosan lehet új kéréseket indítani a kapcsolaton.

  • Az érvénytelenítési kezelő – akkor hívódik, ha az érvénytelenítési metódus meghívásra kerül, vagy ha egy XPC-segédprogramot nem sikerült elindítani. Amikor ez a kezelő meghívásra kerül, a helyi kapcsolatobjektum már nem érvényes, és újra kell létrehozni. Ez mindig az utolsó kezelő, amelyet egy kapcsolati objektumon hívnak meg. Amikor ez a blokk meghívásra kerül, a kapcsolatobjektum lebontásra került. Ekkor már nem lehet további üzeneteket küldeni a kapcsolaton, sem a kezelőn belül, sem máshol a kódban.

Mindkét esetben blokk-határolt változókat kell használnia, hogy elegendő kontextuális információt – esetleg egy függőben lévő műveletsort és magát a kapcsolatobjektumot – adjon meg, hogy a kezelő kódja valami értelmeset tehessen, például újrapróbálja a függőben lévő műveleteket, lebontsa a kapcsolatot, hiba párbeszédpanelt jelenítsen meg, vagy bármilyen más, az adott alkalmazásban értelmes műveletet.