我试图在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];
答案 0 :(得分:16)
背景信息(类定义与类实例)
这里重要的概念是类定义和类实例之间的区别。
类定义是该类的源代码。例如,ViewController.m
包含myViewController
类的定义,AppDelegate.m
包含AppDelegate
类的定义。你问题中提到的另一个类是UIApplication
。这是一个系统定义的类,即你没有该类的源代码。
类实例是堆上的一块内存,以及指向该内存的指针。通常使用像
这样的代码行创建类实例myClass *foo = [[myClass alloc] init];
请注意alloc
为类保留堆上的空间,然后init
设置类的变量/属性的初始值。然后,指向实例的指针存储在foo
。
当您的应用程序启动时,会发生以下事件序列(粗略地说):
delegate
存储指向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 /故事板为您实例化的视图控制器的引用。