从AppDelegate调用方法 - Objective-C

时间:2014-05-03 13:10:43

标签: ios iphone objective-c uiviewcontroller appdelegate

我试图在ViewController.m方法中从AppDelegate.m调用applicationDidEnterBackground的现有方法,因此我找到了这个链接: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];

3 个答案:

答案 0 :(得分:16)

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

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

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

类实例是堆上的一块内存,以及指向该内存的指针。通常使用像

这样的代码行创建类实例
myClass *foo = [[myClass alloc] init];

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

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

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

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

意大利面条代码解决方案

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

另一个主题建议使用意大利面条代码解决问题。 (请注意,建议使用意大利面条代码方法只是因为其他主题中的OP并不想通知通知。)以下是意大利面条代码的工作原理

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

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

当系统调用applicationDidEnterBackground时,可以使用指向MyViewController实例的指针。

正确的解决方案

正确的解决方案是使用通知(如kkumpavat&#39; s)

- (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];
}

通过通知,您不会将冗余指针存储到视图控制器中,并且您不必弄清楚系统将指针存储到视图控制器的位置。通过致电addObserver UIApplicationDidEnterBackgroundNotification,您告诉系统直接调用视图控制器的didEnterBackground方法。

答案 1 :(得分:6)

这里有两个问题。

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

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

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod) name:UIApplicationDidEnterBackgroundNotification object:nil];

    //Your implementation
}

-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

答案 2 :(得分:4)

视图控制器已经实例化为NIB /故事板过程的一部分,因此如果您的应用程序委托自己执行alloc / init,则只需创建另一个实例(与之无关)一个人创建了NIB /故事板。)

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