【问题标题】:Call a method from AppDelegate - Objective-C从 AppDelegate 调用方法 - Objective-C
【发布时间】:2014-06-20 03:00:45
【问题描述】:

我试图在applicationDidEnterBackground 方法中从AppDelegate.m 调用我的ViewController.m 的现有方法,所以我找到了这个链接:Calling UIViewController method from app delegate,它告诉我要实现这个代码:

在我的 ViewController.m 中

-(void)viewDidLoad
{
    [super viewDidLoad];

    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    appDelegate.myViewController = self;
}

在我的 AppDelegate 中:

@class MyViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (weak, nonatomic) MyViewController *myViewController;

@end

在 AppDelegate 的实现中:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self.myViewController method];
}

所以我把这段代码放在我的项目中,它运行良好,但我不明白代码是如何工作的,一行一行。 sharedApplication 有什么作用?为什么我必须设置一个委托而不是仅仅创建一个 ViewController 的实例,比如:

ViewController * instance = [[ViewController alloc] init];
[instance method];

【问题讨论】:

    标签: ios iphone objective-c uiviewcontroller appdelegate


    【解决方案1】:

    视图控制器已经作为 NIB/storyboard 进程的一部分被实例化了,所以如果你的应用程序委托有自己的alloc/init,你只是在创建另一个实例(它与创建的那个没有关系笔尖/故事板)。

    您概述的构造的目的仅仅是为应用委托提供对 NIB/故事板为您实例化的视图控制器的引用。

    【讨论】:

      【解决方案2】:

      你有两个问题。

      1) sharedApplication 做什么?
      [UIApplication sharedApplication] 为您提供属于您的应用程序的 UIApplication 实例。这是您应用程序的集中控制点。有关更多信息,您可以阅读 iOS 开发者网站上的UIApplication class reference

      2) 为什么我必须设置委托而不是仅仅创建 ViewController 的实例?
      再次使用alloc/initAppDelegate 中创建控制器将创建新实例,并且这个新实例并不指向您所指的控制器。所以你不会得到你正在寻找的结果。
      但是,在applicationDidEnterBackground 的这个特殊用例中,您不需要在AppDelegate 中引用您的控制器。您的 ViewController 可以在 viewDidLoad 函数中注册 UIApplicationDidEnterBackgroundNotification 通知并在 dealloc 函数中取消注册。

      - (void)viewDidLoad
      {
          [super viewDidLoad];
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod) name:UIApplicationDidEnterBackgroundNotification object:nil];
      
          //Your implementation
      }
      
      -(void)dealloc{
          [[NSNotificationCenter defaultCenter] removeObserver:self];
      }
      

      【讨论】:

        【解决方案3】:

        背景信息(类定义与类实例)

        这里的重要概念是类定义和类实例之间的区别。

        类定义是类的源代码。例如ViewController.m 包含myViewController 类的定义,AppDelegate.m 包含AppDelegate 类的定义。您的问题中提到的另一个类是UIApplication。那是一个系统定义的类,即你没有该类的源代码。

        类实例是堆上的一块内存,以及指向该内存的指针。类实例通常是用这样的一行代码创建的

        myClass *foo = [[myClass alloc] init];
        

        注意alloc在堆上为类保留空间,然后init为类的变量/属性设置初始值。然后将指向该实例的指针存储在foo

        当您的应用程序启动时,会发生以下事件序列(粗略地说):

        • 系统创建一个 UIApplication 类的实例
        • 指向 UIApplication 实例的指针存储在 系统变量
        • 系统创建 AppDelegate 类的实例
        • 指向 AppDelegate 的指针存储在一个名为 delegate 在 UIApplication 实例中
        • 系统创建 MyViewController 类的一个实例
        • 指向 MyViewController 类的指针存储在某处

        指向 MyViewController 的指针的存储是事情变得混乱的地方。 AppDelegate 类有一个名为window 的UIWindow 属性。 (您可以在 AppDelegate.h 中看到。)如果应用程序只有一个视图控制器,则指向该视图控制器的指针存储在 window.rootViewController 属性中。但是如果应用程序有多个视图控制器(在 UINavigationController 或 UITabBarController 下),那么事情就会变得复杂。

        意大利面条代码解决方案

        那么你面临的问题是:当系统调用applicationDidEnterBackground 方法时,你如何获得指向视图控制器的指针?好吧,从技术上讲,应用程序委托在window 属性下的某处有一个指向视图控制器的指针,但是没有简单的方法来获取该指针(假设应用程序有多个视图控制器)。

        另一个线程建议使用意大利面条代码方法来解决该问题。 (请注意,建议使用意大利面条代码方法只是因为该其他线程中的 OP 不想正确处理通知。)这是意大利面条代码的工作原理

        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
        appDelegate.myViewController = self;
        

        此代码检索指向系统创建的 UIApplication 实例的指针,然后查询delegate 属性以获取指向 AppDelegate 实例的指针。指向 self 的指针,即指向 MyViewController 实例的指针,然后存储在 AppDelegate 的属性中。

        然后可以在系统调用applicationDidEnterBackground时使用指向 MyViewController 实例的指针。

        正确的解决方案

        正确的解决方案是使用通知(如 kkumpavat 的回答)

        - (void)viewDidLoad
        {
            [super viewDidLoad];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
        }
        
        - (void)didEnterBackground
        {
            NSLog( @"Entering background now" );
        }
        
        -(void)dealloc
        {
            [[NSNotificationCenter defaultCenter] removeObserver:self];
        }
        

        使用通知,您无需存储指向视图控制器的冗余指针,也无需弄清楚系统将指向视图控制器的指针存储在何处。通过为UIApplicationDidEnterBackgroundNotification 调用addObserver,您是在告诉系统直接调用视图控制器的didEnterBackground 方法。

        【讨论】:

        • 非常感谢!非常完整的答案!
        猜你喜欢
        • 2017-10-15
        • 2023-03-29
        • 1970-01-01
        • 2011-05-26
        • 1970-01-01
        • 2012-02-09
        • 1970-01-01
        • 2014-03-22
        • 2012-07-31
        相关资源
        最近更新 更多