Remote View Controllers in iOS 6(翻译)
Remote View Controllers in iOS 6(翻译)
Lambda8421 发表于3年前
Remote View Controllers in iOS 6(翻译)
  • 发表于 3年前
  • 阅读 19
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

Remote View Controllers in iOS 6

    在我之前的一篇文章previous article on sharing in iOS 6中,我曾经暗示,苹果公司可能正在寻找一个在不损害iOS安全结构的强大的方法使不同的app之间分享内容。

    实际上,苹果公司在iOS 6中已经在用一个没有正式声明的概念(Remote View Controller)。这个方法是正在尝试在iOS 6底层的一种探索,可能会出现在以后的iOS版本中。

    我第一次知道Remote View Controller的存在是通过tweet上的Grant Paul:

    重大新闻,苹果公司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不会出现启动新的进程。

    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上通信。

 

共有 人打赏支持
粉丝 11
博文 120
码字总数 121640
×
Lambda8421
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: