在我以前的一篇文章previous article on sharing in iOS 6中,我曾經暗示,蘋果公司可能正在尋找一個在不損害iOS安全結構的強大的方法使不一樣的app之間分享內容。 html
實際上,蘋果公司在iOS 6中已經在用一個沒有正式聲明的概念(Remote View Controller)。這個方法是正在嘗試在iOS 6底層的一種探索,可能會出如今之後的iOS版本中。 ios
我第一次知道Remote View Controller的存在是經過tweet上的Grant Paul: web
重大新聞,蘋果公司iOS 6私有特性Remote View Controller正在被使用,例如:Mail compose view如今是分進程運行。 瀏覽器
這個消息給了咱們一些探索的方向的線索。我經過iOS 6中四個內置的分享按鍵寫了一個很是簡單的測試app。我用蘋果iOS 6的官方API來實現這個目的。下面這個例子的代碼呈現的是e-mail分享視圖: 安全
- (IBAction)openMailComposer:(id)sender { if (![MFMailComposeViewController canSendMail]) { return; } MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init]; [controller setMailComposeDelegate:self]; [self presentViewController:controller animated:YES completion:nil]; }
當咱們在Activity Monitor中運行這個app的時候,咱們會發現一個叫作MailCompositionService新的線進程啓動。進一步觀察發現,這個進程是屬於/Applications/MailCompositionService.app/,連接到/System/Library/PrivateFrameworks/XPCObjects.framework/。 網絡
Activity Monitor 顯示了MailCompositionService在app在iOS 6中啓動時調用 MFMailComposeViewController。視圖窗口還代表MailCompositionService 連接到私有的XPCObjects.framework。 架構
若是你想證實這件事你不用本身寫一個app。任何提供給用戶經過e-mail分析內容的iOS app都會顯示一樣的結果。這個行爲是iOS 6的最新特性。在iOS 5上運行一樣的app不會出現啓動新的進程。 app
XPC(https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html#//apple_ref/doc/uid/10000172i-SW6-SW1) 框架
MailCompositionService.app連接的框架是說明蘋果公司正在iOS上使用XPC的第一個線索。在OS X 10.7的介紹中,XPC定義了一個一種簡單而有效的異步方式去實現進程間相互通訊。 異步
XPC的另一個特性就是操做系統管理XPC程序的生命週期。主機App不須要去管理開始和結束一個服務程序;主機App只須要打開XPC連接到服務程序,OS管理XPC程序,當須要的時候啓動,當全部的鏈接結束後退出。
蘋果公司在OS X中加入XPC是出於安全的考慮。Apps將本身分離成幾個獨立的服務,每一個服務本身處理對安全敏感的組件。例如,一個web瀏覽器用單獨的XPC 服務分離它的全部網絡交互和它的HTML/JS解析組件。這樣,他們每一個服務只能在有限的權限內運行,若是其中一個受到安全危害時其餘服務則不會有事。
在iOS中,apps已經有很是有限的設置權限。這樣看來,用多個XPC服務分離iOS apps是沒有必要的。可是XPC架構也能夠用於容許現有的apps以一種更安全的方式去訪問全系統服務,甚至容許第三方應用分享彼此的數據而不影響OS的安全模型。
一個class dump揭示了在iOS 6中的確包含了私有的XPCKit.framework3,XPCObjects.framework 和XPCService.framework。這些功能估計會在OS X 10.8中變得更加的優雅和實用。
如今讓咱們來研究e-mail組成表格中的視圖層次。我在代理方法mailComposeController:didFinishWithResult:error: 這裏設置了一個斷點,以便在咱們調試app過程當中一點擊MFMailComposeViewController的按鈕就中止app。
(lldb) po controller (MFMailComposeViewController *) $1 = 0x1e04f6d0 <MFMailComposeViewController: 0x1e04f6d0> //這裏不要驚訝,視圖控制確實是MFMailComposeViewController的實例,下面讓咱們看看四層視圖 (lldb) po [controller.view recursiveDescription] (id) $2 = 0x1e05c2e0 'MFMailComposeViewController:0x1e04f6d0' 1 child[MFMailComposeInternalViewController:0x1e02dfd0 ] <UILayoutContainerView: 0x1e04ffe0; frame = (0 0; 320 480); autoresize = W+H; layer = <CALayer: 0x1e0500a0>> | <UINavigationTransitionView: 0x1d57f6a0; frame = (0 0; 320 480); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x1d57f770>> | | <UIViewControllerWrapperView: 0x1e04f600; frame = (0 20; 320 460); autoresize = W+H; layer = <CALayer: 0x1e0241f0>> | | | 'MFMailComposeInternalViewController:0x1e02dfd0' 1 child[MFMailComposeRemoteViewController:0x1e055230 ] <UIView: 0x1e05f9a0; frame = (0 0; 320 460); autoresize = W+H; layer = <CALayer: 0x1e05fa00>> | | | | 'MFMailComposeRemoteViewController:0x1e055230' <_UISizeTrackingView: 0x1e05c030; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x1e05c110>> | | | | | <_UIRemoteView: 0x1e05c300; frame = (0 0; 320 480); transform = [0.5, -0, 0, 0.5, -0, 0]; userInteractionEnabled = NO; layer = <CALayerHost: 0x1e05c460>>如今就很是有趣了。視圖(控制器)層次由最上面的MFMailComposeInternalViewController一直到MFMailComposeRemoteViewController,最下面是一個_UIRemoteView。在組成mail用戶界面沒有任何的標籤、文本可見。(給人的感受好像是這個視圖屬於不一樣的進程)
與iOS 5的不一樣
相同的apps運行在iOS 5設備上以下所示
(lldb) po controller (MFMailComposeViewController *) $1 = 0x07ea0420 <MFMailComposeViewController: 0x7ea0420> (lldb) po [controller.view recursiveDescription] (id) $2 = 0x08bb4d50 'MFMailComposeViewController:0x7ea0420' 1 child[MFMailComposeController:0x89b0220 ] <UILayoutContainerView: 0x8b97e70; frame = (0 0; 320 480); autoresize = W+H; layer = <CALayer: 0x8b97ec0>> | <UINavigationTransitionView: 0x89b1460; frame = (0 0; 320 480); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x89b1500>> | | <UIViewControllerWrapperView: 0x7e77990; frame = (0 64; 320 416); autoresize = W+H; layer = <CALayer: 0x7e98af0>> | | | 'MFMailComposeController:0x89b0220' <MFMailComposeView: 0x89b4410; baseClass = UITransitionView; frame = (0 0; 320 416); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x89b4530>> | | | | <UIView: 0x89b1d50; frame = (0 0; 320 416); autoresize = W+H; layer = <CALayer: 0x898d130>> | | | | | <MFComposeScrollView: 0x89b4780; baseClass = UIScrollView; frame = (0 0; 320 416); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x89b4940>; contentOffset: {0, 0}> | | | | | | <UIView: 0x89b56d0; frame = (0 0; 320 132); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x89b5700>> | | | | | | | <MFMailComposeRecipientView: 0x89b5d70; frame = (0 0; 320 44); text = ''; autoresize = W; layer = <CALayer: 0x89b5e70>> | | | | | | | | <UIView: 0x89b5500; frame = (0 43; 320 1); autoresize = W; layer = <CALayer: 0x89b5e40>> | | | | | | | | <_MFMailRecipientTextField: 0x89b5fe0; baseClass = UITextField; frame = (45 12; 275 25); text = ''; clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x89b4f30>> | | | | | | | | <MFHeaderLabelView: 0x89b9350; frame = (8 10; 25 21); autoresize = RM+BM; layer = <CALayer: 0x89b9510>> | | | | | | | XX (<MFMailComposeRecipientView: 0x89bc020; frame = (0 44; 320 44); text = ''; alpha = 0; autoresize = W; layer = <CALayer: 0x89bc0b0>>) | | | | | | | | XX (<UIView: 0x89bc100; frame = (0 43; 320 1); autoresize = W; layer = <CALayer: 0x89bc130>>) | | | | | | | | XX (<_MFMailRecipientTextField: 0x89bc240; baseClass = UITextField; frame = (46 12; 274 25); text = ''; clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x89bc360>>) | | | | | | | | XX (<MFHeaderLabelView: 0x89b6420; frame = (8 10; 26 21); autoresize = RM+BM; layer = <CALayer: 0x89b7d80>>) | | | | | | | XX (<MFMailComposeRecipientView: 0x89bd9a0; frame = (0 44; 320 44); text = ''; alpha = 0; autoresize = W; layer = <CALayer: 0x89bda30>>) | | | | | | | | XX (<UIView: 0x89bda80; frame = (0 43; 320 1); autoresize = W; layer = <CALayer: 0x89bdab0>>) | | | | | | | | XX (<_MFMailRecipientTextField: 0x89bdbc0; baseClass = UITextField; frame = (54 12; 266 25); text = ''; clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x89bdce0>>) | | | | | | | | XX (<MFHeaderLabelView: 0x89bede0; frame = (8 10; 34 21); autoresize = RM+BM; layer = <CALayer: 0x89bee20>>) | | | | | | | XX (<MFComposeFromView: 0x89bf580; frame = (0 44; 320 44); alpha = 0; autoresize = W; layer = <CALayer: 0x89bf620>>) | | | | | | | | XX (<UIView: 0x89bf7b0; frame = (0 43; 320 1); autoresize = W; layer = <CALayer: 0x89bf7e0>>) | | | | | | | | XX (<MFHeaderLabelView: 0x89bf520; frame = (8 10; 45 21); autoresize = RM+BM; layer = <CALayer: 0x89bfd10>>) | | | | | | | | XX (<UITextLabel: 0x89d2e70; frame = (57 9; 263 25); text = 'Example User <example@me....'; clipsToBounds = YES; autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x89d2f10>>) | | | | | | | <MFComposeSubjectView: 0x89bffa0; frame = (0 88; 320 44); autoresize = W; layer = <CALayer: 0x89c0020>> | | | | | | | | <UIView: 0x89c01b0; frame = (0 43; 320 1); autoresize = W; layer = <CALayer: 0x89c01e0>> | | | | | | | | <MFHeaderLabelView: 0x89bff20; frame = (8 10; 62 21); autoresize = RM+BM; layer = <CALayer: 0x89bff60>> | | | | | | | | <UITextField: 0x89c0710; frame = (76 12; 236 25); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x89bfff0>> | | | | | | | <MFComposeMultiView: 0x7e97270; frame = (0 44; 320 44); autoresize = W; layer = <CALayer: 0x7e97320>> | | | | | | | | <UIView: 0x7e97500; frame = (0 43; 320 1); autoresize = W; layer = <CALayer: 0x7e97530>> | | | | | | | | <MFHeaderLabelView: 0x7e97700; frame = (8 10; 59 21); autoresize = RM+BM; layer = <CALayer: 0x7e97740>> | | | | | | | | <UILabel: 0x7e97770; frame = (73 9; 239 25); clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x7e978c0>> | | | | | | <MFComposeTextContentView: 0x89cf930; baseClass = UITextContentView; frame = (0 132; 320 284); text = ' Sent from my iPhone'; autoresize = W+H; layer = <CALayer: 0x8bae120>> | | | | | | | <MFComposeBodyField: 0x832f200; baseClass = UIWebDocumentView; frame = (0 0; 320 284); text = ' Sent from my iPhone'; opaque = NO; layer = <UIWebLayer: 0x7e988e0>> | | | | | | | | <TileHostLayer: 0x89b2d70> (layer) | | | | | | | | | <TileLayer: 0x7e985c0> (layer) | | | | | | XX (<UIImageView: 0x8dc6cc0; frame = (1 408; 318 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x8dc6d30>>) - (null) | | | | | | XX (<UIImageView: 0x8dc6d60; frame = (312 1; 7 384); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; animations = { opacity=<CABasicAnimation: 0x89cea00>; }; layer = <CALayer: 0x8dc6dd0>>) - (null) | <UINavigationBar: 0x89b04f0; frame = (0 20; 320 44); autoresize = W; layer = <CALayer: 0x89b0a80>> | | <UINavigationBarBackground: 0x89b0cb0; frame = (0 0; 320 44); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x89b0d40>> - (null) | | <UINavigationItemView: 0x89b11d0; frame = (93 8; 133 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x89b1220>> | | <UINavigationButton: 0x89b1bf0; frame = (5 7; 60 30); opaque = NO; layer = <CALayer: 0x89b1d80>> | | | <UIImageView: 0x89b2590; frame = (0 0; 60 30); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x89b25d0>> - (null) | | | <UIButtonLabel: 0x89b20e0; frame = (10 7; 40 15); text = 'Cancel'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x89b2150>> | | <UINavigationButton: 0x89b2020; frame = (265 7; 50 30); opaque = NO; layer = <CALayer: 0x89b1ae0>> | | | <UIImageView: 0x89b1020; frame = (0 0; 50 30); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x89b2b00>> - (null) | | | <UIButtonLabel: 0x89b1560; frame = (10 7; 30 15); text = 'Send'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x89b15d0>>這個視圖層次有點長,包括了咱們預期的mail全部的標籤和文本。 正如咱們已經看到的, 公共 API 仍然是相同的 , 底層的實現 已經徹底改變了 。 若是你的app 目前 圍繞MFMailComposeViewController這個 公共的 API 試圖 直接 訪問某些 子視圖 , 你可能 已經注意到 它不會 在 iOS 6 中使用了 。
Facebook Sharing
咱們在iOS 6中分享名片能夠獲得一個類似的結果。Activity Monitor將會顯示這個新的程序SocialUIService啓動。視圖(控制器)層次看起來像如下這樣:
(lldb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] presentedViewController] (id) $1 = 0x1d545a10 <SLFacebookComposeViewController: 0x1d545a10> (lldb) po [[[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentedViewController] view] recursiveDescription] (id) $2 = 0x1e02b4f0 'SLFacebookComposeViewController:0x1d545a10' 1 child[SLFacebookRemoteComposeViewController:0x1d5404b0 ] <UIView: 0x1d547f90; frame = (0 20; 320 460); opaque = NO; autoresize = W+H; layer = <CALayer: 0x1d57efa0>> | 'SLFacebookRemoteComposeViewController:0x1d5404b0' <_UISizeTrackingView: 0x1d586c00; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x1d586c60>> | | <_UIRemoteView: 0x1d586ce0; frame = (0 0; 320 480); transform = [0.5, -0, 0, 0.5, -0, 0]; userInteractionEnabled = NO; layer = <CALayerHost: 0x1d586d40>>
SLFacebookComposeViewController只有一個子視圖SLFacebookRemoteComposeViewController,咱們沒有關於remote view的內容的信息。
Twitter Sharing 沒有轉化爲XPC
有趣的是,我在用SLComposeViewController在Tweet中分析名片時觀察到的是不一樣的行爲。Tweet分享名片時也會觸發一個新的進程,twitterd,全部的視圖層次中包含了預期的全部的子視圖,可是沒有找到_UIRemoteView的蹤影。
我想twitterd只負責管理在iOS設置中Twitter帳戶的登陸。我猜想蘋果公司尚未將Tweet中分享名片轉化成新的Remote View Controller模式。
仔細研究MailCompositionService
讓咱們看看在Activity Monitor中新的進程裏咱們發現哪些東西。他們的iOS 模擬器二進制文件在
Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk/Applications. //在這個目錄裏,咱們找到了 MailCompositionService.app MessagesViewService.app (短消息) SocialUIService.app (社會化分享,像facebook).MailCompositionService.app 繼承自如下類和協議
The MFMailComposeRemoteService and MFMailComposeRemoteHost protocols ComposeNavigationController, a UINavigationController subclass. ComposeServiceRemoteViewController, a UIViewController subclass that implements, among others, the MFMailComposeRemoteService protocol. This class contains an ivar XPCProxy<MFMailComposeRemoteHost> *_proxy;.不須要太多的實現細節,咱們能夠清晰的發現主機app程序和服務在進程的邊界都設置了代理。這些代理用XPC項目通訊。MFMailComposeRemoteService 和 MFMailComposeRemoteHost這兩個協議定義了短消息能被髮送的目標。
主機app也能夠在mail分享名片中用setCompositionValues:, setUICustomizationData: 和 定義在 MFMailComposeRemoteService中的addAttachmentData:mimeType:fileName:identifier:消息初始化數據。
MailCompositionService也能夠用定義在MFMailComposeRemoteHost中的bodyFinishedDrawing 和 compositionFinishedWithResult:error:消息在主機app上通訊。