Remote View Controllers in iOS 6(翻译)

Remote View Controllers in iOS 6

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

相关文章
相关标签/搜索