简体   繁体   English

从AppDelegate调用方法 - Objective-C

[英]Call a method from AppDelegate - Objective-C

I was trying to call an existing method of my ViewController.m from AppDelegate.m inside the applicationDidEnterBackground method, so I found this link: Calling UIViewController method from app delegate , which told me to implement this code: 我试图从applicationDidEnterBackground方法中的AppDelegate.m调用我的ViewController.m的现有方法,所以我找到了这个链接: 从app delegate调用UIViewController方法 ,它告诉我实现这段代码:

In my ViewController.m 在我的ViewController.m中

-(void)viewDidLoad
{
    [super viewDidLoad];

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

In my AppDelegate: 在我的AppDelegate中:

@class MyViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (weak, nonatomic) MyViewController *myViewController;

@end

And in the AppDelegate's implementation: 在AppDelegate的实现中:

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

So I put this code in my project and it worked fine, but I didn't understand how the code works, line by line. 所以我把这个代码放在我的项目中,它工作得很好,但我不明白代码如何工作,一行一行。 What does the sharedApplication do? sharedApplication什么作用? Why must I set a delegate instead of just creating an instance of ViewController, like: 为什么我必须设置一个委托而不是只创建一个ViewController实例,如:

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

Background information (class definition vs class instance) 背景信息(类定义与类实例)

The important concept here is the difference between a class definition and a class instance. 这里重要的概念是类定义和类实例之间的区别。

The class definition is the source code for the class. 类定义的源代码。 For example ViewController.m contains the definition for the myViewController class, and AppDelegate.m contains the definition for the AppDelegate class. 例如, ViewController.m包含myViewController类的定义, AppDelegate.m包含AppDelegate类的定义。 The other class mentioned in your question is UIApplication . 你问题中提到的另一个类是UIApplication That is a system-defined class, ie you don't have the source code for that class. 这是一个系统定义的类,即您没有该类的源代码。

A class instance is a chunk of memory on the heap, and a pointer to that memory. 类实例是堆上的一块内存,以及指向该内存的指针。 A class instance is typically created with a line of code like this 通常使用这样的代码行创建类实例

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

Note that alloc reserves space on the heap for the class, and then init sets the initial values for the variables/properties of the class. 请注意, alloc为类的堆保留空间,然后init设置类的变量/属性的初始值。 A pointer to the instance is then stored in foo . 然后将指向实例的指针存储在foo

When your application starts, the following sequence of events occurs (roughly speaking): 当您的应用程序启动时,会发生以下事件序列(粗略地说):

  • the system creates an instance of the UIApplication class 系统创建UIApplication类的实例
  • the pointer to the UIApplication instance is stored somewhere in a system variable 指向UIApplication实例的指针存储在系统变量的某个位置
  • the system creates an instance of the AppDelegate class 系统创建AppDelegate类的实例
  • the pointer to the AppDelegate is stored in a variable called delegate in the UIApplication instance 指向AppDelegate的指针存储在UIApplication实例中名为delegate的变量中
  • the system creates an instance of the MyViewController class 系统创建MyViewController类的实例
  • the pointer to the MyViewController class is stored somewhere 指向MyViewController类的指针存储在某处

The storage of the pointer to MyViewController is where things get messy. 存储指向MyViewController的指针是事情变得混乱的地方。 The AppDelegate class has a UIWindow property called window . AppDelegate类有一个名为window的UIWindow属性。 (You can see that in AppDelegate.h.) If the app only has one view controller, then the pointer to that view controller is stored in the window.rootViewController property. (您可以在AppDelegate.h中看到。)如果应用程序只有一个视图控制器,那么指向该视图控制器的指针将存储在window.rootViewController属性中。 But if the app has multiple view controllers (under a UINavigationController or a UITabBarController) then things get complicated. 但是如果应用程序有多个视图控制器(在UINavigationController或UITabBarController下),那么事情会变得复杂。

The spaghetti code solution 意大利面条代码解决方案

So the issue that you face is this: when the system calls the applicationDidEnterBackground method, how do you get the pointer to the view controller? 所以你面临的问题是:当系统调用applicationDidEnterBackground方法时,你如何获得指向视图控制器的指针? Well, technically, the app delegate has a pointer to the view controller somewhere under the window property, but there's no easy way to get that pointer (assuming the app has more than one view controller). 好吧,从技术上讲,app委托在window属性下的某个地方有一个指向视图控制器的指针,但是没有简单的方法来获取该指针(假设该应用程序有多个视图控制器)。

The other thread suggested a spaghetti code approach to the problem. 另一个线程建议使用意大利面条代码来解决问题。 (Note that the spaghetti code approach was suggested only because the OP in that other thread didn't want to do things correctly with notifications.) Here's how the spaghetti code works (请注意,建议使用意大利面条代码方法只是因为其他线程中的OP不希望正确处理通知。)以下是意大利面条代码的工作原理

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

This code retrieves the pointer to the UIApplication instance that the system created, and then queries the delegate property to get a pointer to the AppDelegate instance. 此代码检索指向系统创建的UIApplication实例的指针,然后查询delegate属性以获取指向AppDelegate实例的指针。 The pointer to self , which is a pointer to the MyViewController instance, is then stored in a property in the AppDelegate. 然后,指向self的指针(它是指向MyViewController实例的指针)存储在AppDelegate的属性中。

The pointer to the MyViewController instance can then be used when the system calls applicationDidEnterBackground . 然后,当系统调用applicationDidEnterBackground时,可以使用指向MyViewController实例的指针。

The correct solution 正确的解决方案

The correct solution is to use notifications (as in kkumpavat's answer) 正确的解决方案是使用通知(如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];
}

With notifications, you aren't storing redundant pointers to your view controllers, and you don't have to figure out where the system has stored the pointer to your view controller. 通过通知,您不会将冗余指针存储到视图控制器,也不必确定系统将指针存储到视图控制器的位置。 By calling addObserver for the UIApplicationDidEnterBackgroundNotification you're telling the system to call the view controller's didEnterBackground method directly. 通过调用addObserver for UIApplicationDidEnterBackgroundNotification你告诉系统直接调用视图控制器的didEnterBackground方法。

You have two question here. 你有两个问题。

1) What does the sharedApplication do? 1) sharedApplication什么作用?
The [UIApplication sharedApplication] gives you UIApplication instance belongs to your application. [UIApplication sharedApplication]为您提供属于您的应用程序的UIApplication实例。 This is centralised point of control for you App. 这是您App的集中控制点。 For more information you can read UIApplication class reference on iOS developer site. 有关更多信息,您可以在iOS开发人员站点上阅读UIApplication类参考

2) Why must I set a delegate instead of just creating an instance of ViewController? 2)为什么我必须设置委托而不是仅仅创建ViewController的实例?
Creating controller in AppDelegate again using alloc / init will create new instance and this new instance does not point to the controller you are referring to. 使用alloc / init再次在AppDelegate创建控制器将创建新实例,并且此新实例不会指向您所指的控制器。 So you will not get result you are looking for. 所以你不会得到你想要的结果。
However in this particular use case of applicationDidEnterBackground , you don't need to have reference of you controller in AppDelegate . 但是在applicationDidEnterBackground这个特定用例中,您不需要在AppDelegate引用您的控制器。 You ViewController can register for UIApplicationDidEnterBackgroundNotification notification in viewDidLoad function and unregister in dealloc function. 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];
}

The view controller is already instantiated as part of the NIB/storyboard process, so if your app delegate does its own alloc / init , you are simply creating another instance (which bears no relation to the one created the NIB/storyboard). 视图控制器已经被实例化为NIB /故事板过程的一部分,因此如果您的应用程序委托执行自己的alloc / init ,您只需创建另一个实例(与创建NIB /故事板的实例无关)。

The purpose of the construct you outline is merely to give the app delegate a reference to the view controller that the NIB/storyboard instantiated for you. 您概述的构造的目的仅仅是为应用程序委托对NIB /故事板为您实例化的视图控制器的引用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM