从AppDelegate调用方法 - Objective-C

luc*_*rci 12 iphone objective-c uiviewcontroller ios appdelegate

我试图ViewController.m从方法AppDelegate.m内部 调用my的现有方法applicationDidEnterBackground,所以我找到了这个链接:从app delegate调用UIViewController方法,它告诉我实现这段代码:

在我的ViewController.m中

-(void)viewDidLoad
{
    [super viewDidLoad];

    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    appDelegate.myViewController = self;
}
Run Code Online (Sandbox Code Playgroud)

在我的AppDelegate中:

@class MyViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (weak, nonatomic) MyViewController *myViewController;

@end
Run Code Online (Sandbox Code Playgroud)

在AppDelegate的实现中:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self.myViewController method];
}
Run Code Online (Sandbox Code Playgroud)

所以我把这个代码放在我的项目中,它工作得很好,但我不明白代码如何工作,一行一行.怎么sharedApplication办?为什么我必须设置一个委托而不是只创建一个ViewController实例,如:

ViewController * instance = [[ViewController alloc] init];
[instance method];
Run Code Online (Sandbox Code Playgroud)

use*_*109 17

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

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

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

类实例是对堆内存块,和一个指针存储器.通常使用这样的代码行创建类实例

myClass *foo = [[myClass alloc] init];
Run Code Online (Sandbox Code Playgroud)

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

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

  • 系统创建UIApplication类的实例
  • 指向UIApplication实例的指针存储在系统变量的某个位置
  • 系统创建AppDelegate类的实例
  • 指向AppDelegate的指针存储在delegateUIApplication实例中调用的变量 中
  • 系统创建MyViewController类的实例
  • 指向MyViewController类的指针存储在某处

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

意大利面条代码解决方案

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

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

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.myViewController = self;
Run Code Online (Sandbox Code Playgroud)

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

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

正确的解决方案

正确的解决方案是使用通知(如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];
}
Run Code Online (Sandbox Code Playgroud)

通过通知,您不会将冗余指针存储到视图控制器,也不必确定系统将指针存储到视图控制器的位置.通过调用addObserverUIApplicationDidEnterBackgroundNotification你告诉系统调用视图控制器的didEnterBackground直接方法.


kku*_*vat 8

你有两个问题.

1)sharedApplication做什么?
[UIApplication sharedApplication]给你UIApplication的实例属于您的应用程序.这是您App的集中控制点.有关更多信息,您可以在iOS开发人员站点上阅读UIApplication类参考.

2)为什么我必须设置委托而不是仅创建ViewController的实例?使用/ 再次
创建控制器将创建新实例,并且此新实例不指向您所指的控制器.所以你不会得到你想要的结果. 但是在这个特定的用例中,你不需要参考你的控制器.ViewController可以在函数中注册通知并在函数中取消注册.AppDelegateallocinit
applicationDidEnterBackgroundAppDelegateUIApplicationDidEnterBackgroundNotificationviewDidLoaddealloc

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

    //Your implementation
}

-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Run Code Online (Sandbox Code Playgroud)