XPC

關於 XPC XPC 是 OS X 下的一種 IPC (進程間通訊) 技術, 它實現了權限隔離, 使得 App Sandbox 更加完備.html

首先,XPC 更多關注的是實現功能某種的方式,一般採用其餘方式一樣可以實現。並無強調若是不使用 XPC,沒法實現某些功能。git

XPC 目的是提升 App 的安全性和穩定性。XPC 讓進程間通訊變得更容易,讓咱們可以相對容易地將 App 拆分紅多個進程的模式。更進一步的是,XPC 幫我管理了這些進程的生命週期,當咱們須要與子進程通訊的時候,子進程已經被 XPC 給運行起來了。github

咱們將使用在頭文件 NSXPCConnection.h 中聲明的 Foundation framework API,它是創建在頭文件 xpc/xpc.h 中聲明的原始 XPC API 之上的。XPC API 本來是純 C 實現的 API,很好地集成了 libdispatch(又名 GCD)。本文中咱們將使用Foundation 中的類,它們可讓咱們使用 XPC 的幾乎所有功能(真實的表現了實際底層 C API 是如何工做的),同時與 C API 相比,Foundation API 使用起來會更加容易。面試

小編這裏推薦一個羣:691040931 裏面有大量的書籍和麪試資料,不少的iOS開發者都在裏面交流技術

哪些地方用到了 XPC ? Apple 在操做系統的各個部分普遍使用了 XPC,不少系統 Framework 也利用了 XPC 來實現其功能。你能夠在命令行運行以下搜索命令:swift

% find /System/Library/Frameworks -name \*.xpc
複製代碼

結果顯示 Frameworks 目錄下有 55 個 XPC service(譯者注:在 Yosemite 下),範圍從 AddressBook 到 WebKit 等等。數組

若是在 /Applications 目錄下作一樣的搜索,咱們會發現從 iWork 套件到 Xcode,甚至是一些第三方應用程序都使用了 XPC。安全

Xcode 自己就是使用 XPC 的一個很好的例子:當你在 Xcode 中編輯 Swift 代碼的時候,Xcode 就是經過 XPC 與 SourceKit 通訊的(譯者注:實際進程名應該是SourceKitService)。SourceKit 是主要負責源代碼解析,語法高亮,排版,自動完成等功能的 XPC service。更多詳情能夠參考 JP Simard 的博客.bash

其實 XPC 在 iOS 上應用的很普遍 - 可是目前只有 Apple 可以使用,第三方開發者還不能使用。服務器

一個示例 App

讓咱們來看一個簡單的示例:一個在 table view 中顯示多張圖片的 App。圖片是以 JPEG 格式從網絡服務器上下載下來的。網絡

App看起來是這樣:

NSTableViewDataSource 會從 ImageSet 類加載圖片 ,像這樣:

func tableView(tableView: NSTableView!, viewForTableColumn tableColumn: NSTableColumn!, row: Int) -> NSView! {
    let cellView = tableView.makeViewWithIdentifier("Image", owner: self) as NSTableCellView
    var image: NSImage? = nil
    if let c = self.imageSet?.images.count {
        if row < c {
            image = self.imageSet?.images[row]
        }
    }
    cellView.imageView.image = image
    return cellView
}
複製代碼

ImageSet 類有一個簡單的屬性:

var images: NSImage![]
複製代碼

ImageLoader 類會異步的填充這個圖片數組。

不使用XPC

若是不使用XPC,咱們能夠這樣實現 ImageLoader 類來下載並解壓圖片:

class ImageLoader: NSObject {
    let session: NSURLSession

    init()  {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        session = NSURLSession(configuration: config)
    }

    func retrieveImage(atURL url: NSURL, completionHandler: (NSImage?)->Void) {
        let task = session.dataTaskWithURL(url) {
            maybeData, response, error in
            if let data: NSData = maybeData {
                dispatch_async(dispatch_get_global_queue(0, 0)) {
                    let source = CGImageSourceCreateWithData(data, nil).takeRetainedValue()
                    let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil).takeRetainedValue()
                    var size = CGSize(
                        width: CGFloat(CGImageGetWidth(cgImage)),
                        height: CGFloat(CGImageGetHeight(cgImage)))
                    let image = NSImage(CGImage: cgImage, size: size)
                    completionHandler(image)
                }
            }
        }
        task.resume()
    }
}
複製代碼

明確並且工做得很好。

錯誤隔離 (Fault Isolation) 和 權限隔離 (Split Privileges) 咱們的 App 作了三件不一樣的事情:從互聯網上下載數據,解碼爲 JPEG,而後顯示。

若是把 App 拆分紅三個獨立的進程,咱們就能給每一個進程單獨的權限了;UI 進程並不須要訪問網絡的權限。圖片下載的進程的確須要訪問網絡,但它不須要訪問文件的權限(它只是轉發數據,並不作保存)。而將 JPEG 圖片解碼爲 RGB 數據的進程既不須要訪問網絡的權限,也不須要訪問文件的權限。

經過這種方式,在咱們的 App 中尋找安全漏洞的行爲已經變得很困難了。另外一個好處是,咱們的 App 會變得更穩定;例以下載 service 由於 bug 致使的 crash 並不會影響 App 主進程的運行;而下載 service 會被重啓。

架構圖以下:

XPC 的使用十分靈活,咱們還能夠這樣設計:讓 App 直接和兩個 service 通訊,由 App 來負責 service 之間的數據交互。後面咱們會看到 App 是如何 找到 XPC services的。

迄今爲止,大部分安全相關的 bug 都出如今解析不受信數據的過程中,例如數據是咱們從互聯網上接收到的,不受咱們控制的。現實中 HTTP 協議和解析 JPEG 數據也須要處理這樣的問題,而經過這樣設計,咱們將解析不受信數據的過程挪進了一個子進程,即一個 XPC service。

在 App 中使用 XPC Services

XPC service 由兩個部分組成:service 自己,以及與之通訊的代碼。它們都很簡單並且類似,算是個好消息。

在 Xcode 中有模板能夠添加新的 XPC service target。 每一個 service 都須要一個 bundle id,一個好的實踐是將其設置爲 App 的 bundle id 的 subdomain(子域)。

在咱們的例子中,App 的 bundle id 是 io.objc.Superfamous-Images,咱們能夠把下載 service 的 bundle id 設爲 io.objc.Superfamous-Images.ImageDownloader

在 build 過程當中,Xcode 會爲 service target 建立一個獨立 bundle,這個 bundle 會被複制到 XPCServices 目錄下,與 Resources 目錄平級。

當 App 將數據發給 io.objc.Superfamous-Images.ImageDownloader 這個 service 時,XPC 會自動啓動這個 service。

基於 XPC 的通訊基本都是異步的。咱們經過一個 App 和 service 都使用的 protocol 來進行定義。在咱們的例子中:

@objc(ImageDownloaderProtocol) protocol ImageDownloaderProtocol {
    func downloadImage(atURL: NSURL!, withReply: (NSData?)->Void)
}
複製代碼

請注意 withReply: 這部分。它代表了消息是如何經過異步的方式回給調用方的。若返回的消息帶有數據,須要將函數簽名最後一部分寫成:withReply: 並接受一個閉包參數的形式。

在咱們的例子中,service 只提供了一個方法;可是咱們能夠在 protocol 裏定義多個方法。

App 到 service 的鏈接是經過建立 NSXPCConnection 對象來完成的,像這樣:

let connection = NSXPCConnection(serviceName: "io.objc.Superfamous-Images.ImageDownloader")
connection.remoteObjectInterface = NSXPCInterface(`protocol`: ImageDownloaderProtocol.self)
connection.resume()
複製代碼

咱們能夠把 connection 對象保存爲 self.imageDownloadConnection,這樣以後就能夠像這樣和 service 進行通訊了:

let downloader = self.imageDownloadConnection.remoteObject as ImageDownloaderProtocol
downloader.downloadImageAtURL(url) {
    (data) in
    println("Got \(data.length) bytes.")
}
複製代碼

咱們還應該給 connection 對象設置錯誤處理函數,像這樣:

let downloader = self.imageDownloadConnection.remoteObjectProxyWithErrorHandler {
        (error) in NSLog("remote proxy error: %@", error)
} as ImageDownloaderProtocol
downloader.downloadImageAtURL(url) {
    (data) in
    println("Got \(data.length) bytes.")
}
複製代碼

這就是 App 端的全部代碼了。

監聽service請求

XPC service 經過 NSXPCListener 對象來監遵從 App 傳入的請求(譯者注:這是 NSXPCListenerDelegate 中可選的方法)。listener 對象會給每一個來自 App 的請求在 service 端建立對應的 connection 對象。

在 main.swift 中,咱們能夠這樣寫:

class ServiceDelegate : NSObject, NSXPCListenerDelegate {
    func listener(listener: NSXPCListener!, shouldAcceptNewConnection newConnection: NSXPCConnection!) -> Bool {
        newConnection.exportedInterface = NSXPCInterface(`protocol`: ImageDownloaderProtocol.self)
        var exportedObject = ImageDownloader()
        newConnection.exportedObject = exportedObject
        newConnection.resume()
        return true
    }
}

// Create the listener and run it by resuming:
let delegate = ServiceDelegate()
let listener = NSXPCListener.serviceListener()
listener.delegate = delegate;
listener.resume()
複製代碼

咱們建立了一個全局(至關於 C 或 Objective-C 中的 main 函數)的 NSXPCListener 對象,並設置了它的 delegate,這樣傳入鏈接就會調用咱們的 delegate 方法了。咱們須要給 connection 設置 App 端中一樣使用的 protocol。最後咱們設置 ImageDownloader 實例,它實際上實現了接口:

class ImageDownloader : NSObject, ImageDownloaderProtocol {
    let session: NSURLSession

    init()  {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        session = NSURLSession(configuration: config)
    }

    func downloadImageAtURL(url: NSURL!, withReply: ((NSData!)->Void)!) {
        let task = session.dataTaskWithURL(url) {
            data, response, error in
            if let httpResponse = response as? NSHTTPURLResponse {
                switch (data, httpResponse) {
                case let (d, r) where (200 <= r.statusCode) && (r.statusCode <= 399):
                    withReply(d)
                default:
                    withReply(nil)
                }
            }
        }
        task.resume()
    }
}
複製代碼

值得注意的一個重要是,NSXPCListener 和 NSXPCConnection 默認是掛起 (suspended) 的。咱們設置好後須要調用它們的 resume 方法來啓動。

GitHub上能夠找到這個簡單的示例App。

監聽者 (Listener),鏈接 (Connection) 和導出對象 (Exported Object)

在 App 端,咱們有一個 connection 對象。每次將數據發給 service 時,咱們須要調用 remoteObjectProxyWithErrorHandler 方法來建立一個遠程對象代理 (remote object proxy)。

而在service端,則多了一層。首先須要一個 listener,用來監聽來自 App 的傳入 connection。App 能夠建立多個 connection,listener 會在 service 端創建相應的 connection 對象。每一個 connection 對象都有惟一的 exported object,在 App 端,經過 remote object proxy 發送的消息就是給它的。

當 App 建立一個到 XPC service 的 connection 時,是 XPC 在管理這個 service 的生命週期,service 的啓動與中止都由 XPC runtime 完成,這對 App 來講是透明的。並且若是 service 由於某種緣由 crash 了,也會透明地被重啓。

App 初始化 XPC connection 的時候,XPC service 並不會啓動,直到 App 實際發送的第一條消息到 remote object proxy 時才啓動。若是當前沒有未結束的響應,系統可能會由於內存壓力或者 XPC service 已經閒置了一段時間而中止這個 service。這種狀況下,App 持有的 connection 對象任然有效,下次再使用這個 connection 對象的時候,XPC 系統會自動重啓對應的 XPC service。

若是 XPC service crash 了,它也會被透明地重啓,而且其對應的 connection 也會一直有效。可是若是 XPC service 是在接收消息時 crash 了的話,App 需用從新發送該消息才能接受到對應的響應。這就是爲何要調用 remoteObjectProxyWithErrorHandler 方法來設置錯誤處理函數了。

這個方法接受一個閉包做爲參數,在發生錯誤的時候被執行。XPC API 保證在錯誤處理裏的閉包或者是消息響應裏的閉包之中,只有一個會被執行;若是消息消息響應裏的閉包被執行了,那麼錯誤處理的就不會被執行,反之亦然。這樣就使得資源清理變得容易了。

忽然終止 (Sudden Termination)

XPC 是經過跟蹤那些是否有仍在處理請求來管理 service 的生命週期的,若是有請求正在運行,對應的 service 不會被中止。若是消息請求的響應尚未被髮送,則這個請求會被認爲是正在運行的。對於那些沒有 reply 的處理程序的請求,只要方法體還在運行,這個請求就會被認爲是正在運行的。

在某些狀況下,咱們可能想告訴 XPC 咱們還有更多的工做要作,咱們可使用 NSProcessInfo 的 API 來實現這一點:

func disableAutomaticTermination(reason: String!)
func enableAutomaticTermination(reason: String!)
複製代碼

若是 XPC service 接受傳入請求並須要在後臺執行一些異步操做,這些 API 就能派上用場了(即告訴系統不但願被忽然終止)。某些狀況下咱們還可能須要調整咱們的 QoS (服務質量)設置。

中斷 (Interruption) 和失效 (Invalidation)

XPC 的最多見的用法是 App 發消息給它的 XPC service。XPC 容許很是靈活的設置。咱們經過下文會了解到,connection 是雙向的,它能夠是匿名監聽者 (anonymous listeners)。若是另外一端消失了(由於 crash 或者是正常的進程終止),這時鏈接將頗有可能變得無效。咱們能夠給 connection 對象設置失效處理函數,若是 XPC runtime 沒法從新建立這個 connection,咱們的失效處理函數將會被執行。

咱們還能夠給 connection 設置中斷處理程序,會在 connection 被中斷的時候會執行,儘管此時 connection 仍然是有效的。

在 NSXPCConnection中 對應的兩個屬性是:

var interruptionHandler: (() -> Void)!
var invalidationHandler: (() -> Void)!
複製代碼

雙向鏈接 (Bidirectional Connections)

一個常常被忽略而又有意思的事實是:connection 是雙向的。可是隻能經過 App 建立到 service 的初始鏈接。service 不能主動建立到 App 的鏈接(見下文的 service lookup)。一旦鏈接已經建好了,兩端均可以發起請求。

正如 service 端給 connection 對象設置了 exportedObject,App 端也能夠這麼作。這樣可讓 service 端經過remoteObjectProxy 來和 App 的 exported object 進行通訊了。值得注意是,XPC service 由系統管理其生命週期,若是沒有未完成的請求,可能會被中止掉(參見上文的 Sudden Termination)。

服務查找 (Service Lookup)

當咱們鏈接到 XPC service 的時候,咱們須要找到鏈接的另外一端。對於使用私有 XPC service 的 App,XPC 會在 App 的 bundle 範圍內經過名字查找。還有其餘的方法來鏈接到 XPC,讓咱們來看看全部的可能性。

XPC Service

假如 App 使用:

NSXPCConnection(serviceName: "io.objc.myapp.myservice")
複製代碼

XPC 會在 App 本身的命名空間 (namespace) 查找名爲 io.objc.myapp.myservice 的service,這樣的 service 僅對當前 App 有效,其餘 App 沒法鏈接。XPC service bundle 要麼是位於 App 的 bundle 裏,要麼是在該 App 使用的 Framework 的 bundle 裏。

Mach Service 另外一個選擇是使用:

NSXPCConnection(machServiceName: "io.objc.mymachservice", options: NSXPCConnectionOptions(0))
複製代碼

這會在當前用戶的登陸會話 (login session) 中查找名爲 io.objc.mymachservice 的service。 咱們能夠在 /Library/LaunchAgents 或 ~/Library/LaunchAgents 目錄下安裝 launch agent,這些 launch agent 也以與 App 裏的 XPC service 幾乎相同的方式來提供 service。因爲 launch agent 會在 per-login session 中啓動的,在同一個登陸會話中運行的多個 App 能夠和同一個 launch agent 進行通訊。

這種方法頗有用,例如狀態欄 (Status Bar) 中的 menu extra 程序(即右上角只有菜單項的 App)須要和 UI App 進行通訊的時候。普通 App 和 menu extra 程序均可以和同一個 launch agent 進行通訊並交互數據。當你須要讓兩個以上的進程須要相互通訊,XPC 能夠是一個很是優雅的方案。

假設咱們要寫一個天氣類的 App,咱們能夠把天氣數據的抓取和解析作成 launch agent 方式的 XPC service。咱們能夠分別建立 menu extra 程序,普通 App,以及通知中心的 Widget 來顯示一樣的天氣數據。它們均可以經過 NSXPCConnection 和同一個 launch agent 進行通訊。

與 XPC service 相同,launch agent 的生命週期也能夠徹底由 XPC 掌控:按需啓動,閒置或者系統內存不足的時候中止。

匿名監聽者 (Anonymous Listeners) 和端點 (Endpoints)

XPC 有經過 connection 來傳遞被稱爲 listener endpoints 的能力。這個概念一開始會讓人很是費解,可是它能夠帶來更大的靈活性。

好比說咱們有兩個 App,咱們但願它們可以過 XPC 來互相通訊,每一個 App 都不知道其餘 App 的存在,但它們都知道相同的一個(共享)launch agent。

這兩個 App 能夠先鏈接到 launch agent。App A 建立一個被稱爲 匿名監聽者 (anonymous listener) 的對象,並經過 XPC 發送一個端點 (endpoint),並由匿名監聽者建立的對象給 launch agent。App B 能夠經過 XPC 在一樣的 launch agent 中拿到這個 endpoint。這時,App B 就能夠直接鏈接到這個匿名監聽者,即 App A。

在 App A 建立一個 anonymous listener:

let listener = NSXPCListener.anonymousListener()
複製代碼

相似於 XPC service 建立普通的 listener。而後從這個 listener 建立一個 endpoint:

let endpoint = listener.endpoint
複製代碼

這個 endpoint 能夠經過 XPC 來傳遞(實現了 NSSecureCoding 協議 )。一旦 App B 獲取到這個 endpoint,它能夠建立到 App A 的 listener 的一個 connection:

let connection = NSXPCConnection(listenerEndpoint: endpoint)
複製代碼

Privileged Mach Service 最後一個選擇是使用:

NSXPCConnection(machServiceName: "io.objc.mymachservice", options: .Privileged)
複製代碼

這種方式和 launch agent 很是相似,不一樣的是建立了到 launch daemon 的 connection。launch agent 進程是 per user 的,它們以用戶的身份運行在用戶的登陸會話 (login session) 中。守護進程 (Daemon) 則是 per machine 的,即便當前多個用戶登陸,一個 XPC daemon 也只有一個實例運行。

若是要運行 daemon 的話,有不少安全相關的問題須要考慮。雖然以 root 權限運行 daemon 是可能的,可是最好是不要這麼這麼作。咱們可能更但願它以一些獨特的用戶身份來運行。具體能夠參考 TN2083 - Designing Secure Helpers and Daemons。大多數狀況,咱們並不須要 root 權限。

文件訪問 (File Access)

假設咱們要建立一個 HTTP 文件下載的 service。咱們須要容許 service 能發起對外的網絡鏈接請求。不太明顯的是,咱們可讓 service 下載寫入文件而不須要訪問任何文件。

它是如何作到的呢,首先咱們在 App 中建立這個將被下載的文件,而後給這個文件建立一個文件句柄 (file handle):

let fileURL = NSURL.fileURLWithPath("/some/path/we/want/to/download/to")
if NSData().writeToURL(fileURL, options:0, error:&error) {
    let maybeHandle = NSFileHandle.fileHandleForWritingToURL(url:fileURL, error:&error)
    if let handle = maybeHandle {
        self.startDownload(fromURL:someURL, toFileHandle: handle) {
            self.downloadComplete(url: someURL)
        }
    }
}


func startDownload(fromURL: NSURL, toFileHandle: NSFileHandlehandle, completionHandler: (NSURL)->Void) -> Void
複製代碼

而後將這個文件句柄傳給 remote object proxy,實際上就是經過 XPC connection 傳給了 service,service 經過這個文件句柄寫入內容,就能夠保存到實際的文件中了。

一樣,咱們能夠在一個進程中打開用於讀取數據的 NSFileHandle 對象,而後傳給另一個進程,這樣就能夠作到那個進程不須要直接訪問文件也能讀取其內容了。

移動數據 (Moving Data)

雖然 XPC 很是高效,可是進程間消息傳遞並非免費的。若是你須要經過 XPC 傳遞大量的二進制數據,你可使用這些技巧。

正常狀況下使用的 NSData 對象會在傳遞到另外一端會被複制一份。對於較大的二進制數據,更有效的方法是使用 內存映射 (memory-mapped) 數據。WWDC 2013 session 702 的slides 從 57 頁開始介紹瞭如何發送大量數據

XPC 有個技巧,可以保證數據在進程間傳遞不會被複制。訣竅就是利用 dispatch_data_t 和 NSData 是 toll-free bridged 的。建立內存映射的 dispatch_data_t 實例與之配合,就能夠高效的經過 XPC 來傳遞了。看上去是這樣:

let size: UInt = 8000
let buffer = mmap(nil, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0)
let ddata = dispatch_data_create(buffer, size, DISPATCH_TARGET_QUEUE_DEFAULT, _dispatch_data_destructor_munmap)
let data = ddata as NSData
複製代碼

調試 (Debugging)

Xcode 支持經過 XPC 方式的進程間通訊的調試。若是你在內嵌在 App 的私有 XPC service 裏設置一個斷點,調試器將以你指望的方式在斷點處停下來。

請務必看看 Activity Tracing。這組 API 定義在在頭文件 os/activity.h 中,提供了一種可以跨越上下文執行和進程邊界的傳遞方式,來查看究竟是什麼引發了所須要執行的行爲。WWDC 2014 session 714, Fix Bugs Faster Using Activity Tracing,對此作了很好的介紹。

常見錯誤 (Common Mistakes)

一個最多見的錯誤是沒有調用 connection 或者 listener 的 resume 方法。記得它們建立後都是被掛起狀態。

若是 connection 無效,很大的多是由於配置錯誤致使的。請檢查 bundle id 是否是和 service 名字相匹配,代碼中是否指定了正確的 service 名字。

調試守護進程 (Debugging Daemons)

調試 daemon 會稍微複雜一些,但它仍然能夠很好的工做。daemon會被 launchd 進程啓動。因此須要分兩部設置:在開發過程當中,修改咱們 daemon 的 launchd.plist,設置 WaitForDebugger 爲true。而後在 Xcode 中,修改 daemon 的 scheme,在 scheme editor -> Run -> Info 頁下能夠修改 Launch 方式,從 「Automatically」 改到 「Wait for executable to be launched.」

如今經過 Xcode 運行 daemon,daemon 不會被啓動,可是調試器會一直等着直到它啓動爲止。一旦 launchd啓動了 daemon,調試器會自動鏈接上,咱們就能夠開始幹活了。

Connection 的安全屬性

每一個 NSXPCConnection 具備這些屬性

var auditSessionIdentifier: au_asid_t { get }
var processIdentifier: pid_t { get }
var effectiveUserIdentifier: uid_t { get }
var effectiveGroupIdentifier: gid_t { get }
複製代碼

來描述這個 connection。在 listener 端,如在 agent 或者 daemon 中,能夠利用這些屬性來查看誰在嘗試進行鏈接,能夠基於這些屬性來決定是否容許這些鏈接。對於在 App bundle 裏的私有 XPC service,上面的屬性徹底能夠無視,由於只有當前 App 能夠查找到這個 service。

xpc_connection_create(3) 的 man page 中有一章 「Credentials」,介紹了一些使用這些 API 缺點,在使用時須要多加當心。

QoS 和 Boosts

在 OS X 10.10 中,Apple 提出了 Quality of Service (QoS) 概念。能夠用來輔助調解如給 UI 較高優先級,並下降後臺行爲的優先級。當 QoS 遇到 XPC service,事情就變得有趣了 - 想一想 XPC service 通常是完成什麼樣的工做?

QoS 會跨進程傳送 (propagates),在大多數狀況下咱們都不須要擔憂。當 UI 線程發起一個 XPC 調用時,service 會以 boosted QoS 來運行;可是若是 App 中的後臺線程發起 XPC 調用,這也會影響到 service 的 QoS,它會以較低的 QoS 來運行。

WWDC 2014 session 716, Power, Performance and Diagnostics ,介紹了不少關於 QoS 的內容。其中它就提到了如何使用 DISPATCH_BLOCK_DETACHED 來分離當前的QoS,即如何防止 QoS propagates。

因此當 XPC service 由於某些請求的反作用而開始一些不相關的工做時,必須確保它從 QoS 中分離

低階API (Lower-Level API)

NSXPCConnection 全部的 API 都是創建 C API 之上,能夠在 xpc(3) man page 和子頁面中找到它的文檔。

咱們可使用 C API 來爲 App 建立 XPC service,只要兩端都使用 C API 就好。

在概念上 C API 和 Foundation 的 API 很類似(譯者注:其實是 C API 在 10.7 中被率先引入),稍微使人困惑的一點是,C API 中一個 connection 能夠同時作爲一個接受傳入鏈接請求的 listener ,或者是到另外一個進程的 connection。

事件流 (Event Streams)

目前只有 C API 提供的一個特性是,支持對於 IOKit events,BSD notifications,或者 Core Foundation 的distributed notifications 的 launch-on-demand(按需啓動)。這些在事件或者通知在 launch agent/daemons 也是可使用的。

在 xpc_events(3) man page 中列出了這些事件流。經過 C API,能夠相對簡單的實現一個當特定的硬件鏈接後按需啓動的一個後臺進程 (launch agent)。


小編這裏推薦一個羣:691040931 裏面有大量的書籍和麪試資料,不少的iOS開發者都在裏面交流技術

原文 XPC

相關文章
相關標籤/搜索