Articles

Služby XPC v aplikaci macOS

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

Sledovat

4. září, 2020 – 9 minut čtení

Před XPC jsme vyzvedávali zásuvky a Machovy zprávy (Machovy porty).

Mechanismus XPC nabízí alternativu k zásuvkám (nebo službám Mach pomocí MIG) pro IPC. Můžeme mít například proces, který funguje jako „server“ a čeká na klienty, aby přistoupili k jeho API a poskytli nějakou službu.

Služby XPC na aplikacích

Když mluvíme o službách XPC (velké „S“), máme na mysli balíček nazvaný XPC Service. Svazky v ekosystému Apple označují entity reprezentované určitou adresářovou strukturou. Nejčastěji se setkáte se svazky aplikací. Pokud kliknete pravým tlačítkem myši na libovolnou aplikaci (například Chess.app) a vyberete možnost Zobrazit obsah, najdete adresářovou strukturu. Zpět k XPC, aplikace mohou mít svazky služeb XPC. Najdete je uvnitř adresáře Contents/XPCServices/ uvnitř balíčku aplikace. Yo můžete vyhledat v adresáři /Applications a zjistit, kolik aplikací se spoléhá na služby XPC.

Služby XPC můžete mít také uvnitř rámců (což je další typ svazku).

Další výhody služeb XPC

Používání služeb XPC v našich aplikacích nám umožňuje rozdělit některé funkce do samostatných modulů (The XPC Service). Můžeme vytvořit službu XPC Service, která může mít na starosti spouštění některých nákladných, ale málo častých úloh. Například nějakou kryptografickou úlohu pro generování náhodných čísel.

Další výhodou je, že služba XPC Service běží ve vlastním procesu. Pokud tento proces spadne nebo je zabit, nemá to vliv na naši hlavní aplikaci. Představte si, že vaše aplikace podporuje uživatelsky definované zásuvné moduly. A tyto zásuvné moduly jsou vytvořeny pomocí služby XPC. Pokud jsou špatně nakódované a dojde k jejich pádu, neovlivní to integritu vaší hlavní aplikace.

Další výhodou služby XPC je, že mohou mít vlastní oprávnění. Aplikace bude vyžadovat oprávnění pouze tehdy, když bude využívat službu poskytovanou službou XPC Service, která toto oprávnění vyžaduje. Představte si, že máte aplikaci, která využívá polohu, ale pouze pro určité funkce. Tyto funkce můžete přesunout do služby XPC a přidat oprávnění k poloze pouze do této služby XPC. Pokud váš uživatel nikdy nepotřebuje funkci, která využívá polohu, nebude vyzván k zadání oprávnění, čímž se používání vaší aplikace stane důvěryhodnějším.

XPC a náš přítel launchd

launchd je první proces, který se spouští v našem systému. Má na starosti spouštění a správu dalších procesů, služeb a démonů. launchd má také na starosti plánování úloh. Je tedy logické, že launchd bude zodpovědný také za správu služeb XPC.

Služba XPC může být zastavena, pokud byla dlouho nečinná, nebo může být spuštěna na vyžádání. Veškerou správu provádí launchd a my nemusíme dělat nic, aby to fungovalo.

launchd má informace o dostupnosti prostředků celého systému a vytížení paměti, kdo nejlépe rozhodne o tom, jak co nejefektivněji využít prostředky našeho systému, než launchd

Zavedení služby XPC

Služba XPC je balíček v adresáři Contents/XPCServices hlavního balíčku aplikace; balíček služby XPC obsahuje soubor Info.plist, spustitelný soubor a všechny prostředky potřebné pro službu. Služba XPC určuje, která funkce se má zavolat, když služba obdrží zprávy, voláním xpc_main(3) Mac OS X Developer Tools Manual Page z její hlavní funkce.

Chcete-li vytvořit službu XPC v Xcode, postupujte takto:

  1. Přidejte do projektu nový cíl pomocí šablony služby XPC.
  2. Přidejte do nastavení sestavení své aplikace fázi Kopírovat soubory, která zkopíruje službu XPC do adresáře Obsah/XPCSlužby hlavního svazku aplikace.
  3. Přidejte do nastavení sestavení své aplikace závislost, abyste uvedli, že závisí na svazku služby XPC.
  4. Pokud píšete nízkoúrovňovou službu XPC (založenou na jazyce C), implementujte minimální hlavní funkci pro registraci obsluhy událostí, jak je uvedeno v následujícím výpisu kódu. Nahraďte my_event_handler názvem své funkce obsluhy událostí.
int main(int argc, const char *argv) {
xpc_main(my_event_handler);
// The xpc_main() function never returns.
exit(EXIT_FAILURE);
}

Píšete-li vysokoúrovňovou službu (založenou na Objective-C) pomocí NSXPCConnection, vytvořte nejprve třídu delegáta připojení, která odpovídá protokolu NSXPCListenerDelegate. Poté implementujte minimální funkci main, která vytvoří a nakonfiguruje objekt posluchače, jak ukazuje následující výpis kódu.

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

Použití služby

Způsob použití služby XPC závisí na tom, zda pracujete s rozhraním API jazyka C (služby XPC), nebo s rozhraním API jazyka Objective-C (NSXPCConnection).

Používání rozhraní API Objective-C NSXPCConnection Rozhraní API Objective-C NSXPCConnection poskytuje rozhraní vzdáleného volání procedur na vysoké úrovni, které umožňuje volat metody objektů v jednom procesu z jiného procesu (obvykle aplikace volající metodu ve službě XPC). API NSXPCConnection automaticky serializuje datové struktury a objekty pro přenos a na druhé straně je deserializuje. V důsledku toho se volání metody na vzdáleném objektu chová podobně jako volání metody na lokálním objektu.

Chcete-li používat rozhraní NSXPCConnection API, musíte vytvořit následující:

  • Rozhraní. To se skládá především z protokolu, který popisuje, jaké metody mají být volatelné ze vzdáleného procesu. To je popsáno v části Návrh rozhraní
  • Objekt připojení na obou stranách. Na straně služby to bylo popsáno dříve v části Vytvoření služby. Na straně klienta je to popsáno v části Připojení k rozhraní a jeho použití
  • Posluchač. Tento kód ve službě XPC přijímá připojení. To je popsáno v části Přijímání připojení v pomocném serveru. Zprávy.

Celková architektura

Celková architektura

Při práci s pomocnými aplikacemi založenými na NSXPCConnection mají hlavní aplikace i pomocná aplikace instanci NSXPCConnection. Hlavní aplikace sama vytvoří svůj objekt připojení, což způsobí spuštění pomocné aplikace. Metodě delegáta v pomocné aplikaci je při navázání spojení předán její objekt připojení. To je znázorněno na obrázku 4-1.

Každý objekt NSXPCConnection poskytuje tři klíčové vlastnosti:

  • Vlastnost exportedInterface, která popisuje metody, jež mají být zpřístupněny opačné straně spojení.
  • Vlastnost exportedObject, která obsahuje místní objekt pro zpracování volání metod přicházejících z druhé strany spojení.
  • Možnost získat objekt proxy pro volání metod na druhé straně spojení.

Když hlavní aplikace volá metodu na objektu proxy, objekt NSXPCConnection služby XPC volá tuto metodu na objektu uloženém ve své vlastnosti exportedObject.

Podobně, pokud služba XPC získá objekt proxy a zavolá metodu na tomto objektu, objekt NSXPCConnection hlavní aplikace zavolá tuto metodu na objektu uloženém ve své vlastnosti exportedObject

Návrh rozhraní

Prohlášení API NSXPCConnection využívá protokoly Objective-C k definování programového rozhraní mezi volající aplikací a službou. Každá metoda instance, kterou chcete volat z opačné strany spojení, musí být explicitně definována ve formálním protokolu. Například:

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

Protože komunikace přes XPC je asynchronní, všechny metody v protokolu musí mít návratový typ void. Pokud potřebujete vrátit data, můžete definovat blok odpovědi takto:

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

Metoda může mít pouze jeden blok odpovědi. Protože jsou však spojení obousměrná, může pomocná služba XPC v případě potřeby odpovídat také voláním metod v rozhraní poskytovaném hlavní aplikací.

Každá metoda musí mít návratový typ void a všechny parametry metod nebo bloků odpovědí musí být buď:

  • Aritmetické typy (int, char, float, double, uint64_t, NSUInteger atd.)
  • BOOL
  • C řetězce
  • C struktury a pole obsahující pouze výše uvedené typy
  • Objekty jazyka C, které implementují protokol NSSecureCoding.

Důležité: Pokud má metoda (nebo její blok odpovědí) parametry, které jsou třídami kolekcí Objective-C (NSDictionary, NSArray apod.), a pokud potřebujete v rámci kolekce předávat vlastní objekty, musíte systému XPC explicitně říci, aby tuto třídu povolil jako člen parametru dané kolekce.

Připojení k rozhraní a jeho použití

Pokud jste definovali protokol, musíte vytvořit objekt rozhraní, který jej popisuje. Za tímto účelem zavolejte metodu interfaceWithProtocol: na třídě NSXPCInterface. Například:

NSXPCInterface *myCookieInterface =
;

Po vytvoření objektu rozhraní musíte v rámci hlavní aplikace nakonfigurovat spojení s ním voláním metody initWithServiceName:. Například:

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

Poznámka: Pro komunikaci se službami XPC mimo svazek aplikace můžete také nakonfigurovat spojení s XPC pomocí metody initWithMachServiceName:.

V tomto okamžiku může hlavní aplikace zavolat metody remoteObjectProxy nebo remoteObjectProxyWithErrorHandler: na objektu myConnection, aby získala objekt proxy.

Tento objekt funguje jako proxy pro objekt, který služba XPC nastavila jako svůj exportovaný objekt (nastavením vlastnosti exportedObject). Tento objekt musí vyhovovat protokolu definovanému vlastností remoteObjectInterface.

Když vaše aplikace zavolá metodu na objektu proxy, odpovídající metoda se zavolá na exportovaném objektu uvnitř služby XPC. Když metoda služby volá blok odpovědi, hodnoty parametrů jsou serializovány a odeslány zpět do aplikace, kde jsou hodnoty parametrů deserializovány a předány bloku odpovědi. (Blok odpovědi se provádí v adresovém prostoru aplikace.)

Poznámka: Chcete-li pomocnému procesu povolit volání metod na objektu v aplikaci, musíte před voláním resume nastavit vlastnosti exportedInterface a exportedObject. Tyto vlastnosti jsou dále popsány v následující části.

Přijetí spojení v pomocném procesu

Pokud pomocný proces založený na NSXPCConnection obdrží první zprávu ze spojení, zavolá se metoda listener:shouldAcceptNewConnection: delegáta posluchače s objektem posluchače a objektem spojení. Tato metoda umožňuje rozhodnout, zda spojení přijmout nebo ne; měla by vrátit ANO pro přijetí spojení nebo NE pro odmítnutí spojení.

Poznámka: Pomocník obdrží požadavek na spojení, když je odeslána první skutečná zpráva. Metoda obnovení objektu připojení nezpůsobí odeslání zprávy.

Kromě rozhodování o zásadách musí tato metoda konfigurovat objekt připojení. Zejména za předpokladu, že se pomocník rozhodne přijmout spojení, musí nastavit následující vlastnosti spojení:

  • exportedInterface – objekt rozhraní, který popisuje protokol pro objekt, který chcete exportovat. (Vytvoření tohoto objektu bylo popsáno dříve v části Připojení k rozhraní a jeho použití.)
  • exportedObject – lokální objekt (obvykle v pomocníkovi), kterému mají být doručena volání metod vzdáleného klienta. Kdykoli opačný konec spojení (obvykle v aplikaci) zavolá metodu na proxy objektu spojení, odpovídající metoda se zavolá na objektu určeném vlastností exportedObject.

Po nastavení těchto vlastností by měl před vrácením ANO zavolat metodu resume objektu spojení. Ačkoli delegát může volání resume odložit, spojení nebude přijímat žádné zprávy, dokud tak neučiní.

Posílání zpráv

Posílání zpráv pomocí NSXPC je stejně jednoduché jako volání metody. Například vzhledem k rozhraní myCookieInterface (popsanému v předchozích částech) na objektu připojení XPC myConnection můžete zavolat metodu feedMeACookie takto:

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

Při volání této metody se automaticky zavolá odpovídající metoda v pomocném programu XPC. Tato metoda by zase mohla podobně použít objekt připojení pomocníka XPC k volání metody na objektu exportovaném hlavní aplikací.

Obsluha chyb

Kromě všech metod obsluhy chyb specifických pro danou úlohu pomocníka by služba XPC i hlavní aplikace měly poskytovat také následující bloky obsluhy chyb XPC:

  • Obsluha přerušení – volá se, když proces na druhém konci spojení zkolaboval nebo jinak uzavřel své spojení. Místní objekt spojení je obvykle stále platný – při jakémkoli dalším volání se automaticky vytvoří nová instance pomocníka, pokud to není nemožné – ale může být nutné obnovit jakýkoli stav, který by jinak pomocník udržoval.

Obsluha je volána ve stejné frontě jako zprávy s odpovědí a další obsluhy a je vždy provedena po všech ostatních zprávách nebo obsluhách bloků odpovědí (s výjimkou obsluhy zneplatnění). Z obslužné rutiny přerušení je bezpečné provádět nové požadavky na spojení.

  • Obslužná rutina zneplatnění – volá se při volání metody invalidate nebo když se nepodařilo spustit pomocnou rutinu XPC. Při volání této obslužné rutiny již není lokální objekt připojení platný a musí být znovu vytvořen. Jedná se vždy o poslední handler volaný na objektu připojení. Když je tento blok zavolán, objekt připojení byl zrušen. V tomto okamžiku není možné odesílat další zprávy o připojení, ať už uvnitř obsluhy nebo jinde v kódu.

V obou případech byste měli použít blokově omezené proměnné, abyste poskytli dostatek kontextových informací – možná frontu čekajících operací a samotný objekt připojení – takže kód obsluhy může provést něco smysluplného, například zopakovat čekající operace, zrušit připojení, zobrazit dialogové okno o chybě nebo jiné akce, které mají ve vaší konkrétní aplikaci smysl.