在我以前的一篇文章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上通讯。