简体   繁体   English

实际上,每次有segue转换时都会调用viewDidLoad

[英]viewDidLoad is in fact called every time there is a segue transition

I have seen a lot of posts on stack overflow stating that the viewDidLoad method of controllers is only called the first time the controller is accessed and not necessarily every time but always at least once. 我已经看到很多关于堆栈溢出的帖子说明控制器的viewDidLoad方法仅在第一次访问控制器时被调用,并不一定每次都被调用,但总是至少一次。

This is not what I am seeing at all! 这不是我所看到的! I put together a simple test to highlight this: https://github.com/imuz/ViewDidLoadTest 我整理了一个简单的测试来强调这一点: https//github.com/imuz/ViewDidLoadTest

It seems for navigation controller segues and modal views viewDidLoad is always called. 似乎导航控制器segues和模态视图始终调用viewDidLoad。 The only time it is not called is when switching between tabs. 它没有被调用的唯一时间是在标签之间切换。

Every explanation of viewDidLoad I can find contradicts this: 我发现viewDidLoad的每一个解释都与此相矛盾:

And apples own documentation indicate that a view is only unloaded when memory is low. 苹果自己的文档表明,只有在内存不足时才会卸载视图。

I am currently doing initialisation in viewDidLoad making the assumption that it is called with every segue transition. 我目前正在viewDidLoad中进行初始化,假设它是在每个segue转换时调用的。

Am I missing something here? 我在这里错过了什么吗?

I believe the Apple documentation is describing a situation where the view controller is not being deallocated. 我相信Apple文档描述了视图控制器未被释放的情况。 If you use a segue, then you are causing the instantiation of a new destination controller and, being a new object, it needs to load a view. 如果使用segue,那么您将导致新目标控制器的实例化,并且作为新对象,它需要加载视图。

In xib-based apps, I have sometimes cached a controller object that I knew I might re-use frequently. 在基于xib的应用程序中,我有时会缓存一个我知道可能经常重复使用的控制器对象。 In those cases, they behaved in keeping with the documentation in terms of when a view had to be loaded. 在这些情况下,它们在必须加载视图时的行为与文档保持一致。

Edit: On reading the links you included, I don't see any contradiction in them. 编辑:在阅读您所包含的链接时,我认为它们中没有任何矛盾。 They, too, are talking about things that happen during the lifespan of a view controller object. 他们也在谈论在视图控制器对象的生命周期中发生的事情。

Phillip Mills' answer is correct. Phillip Mills的回答是正确的。 This is just an enhancement of it. 这只是它的增强。

The system is working as documented. 系统正在记录中。

You are seeing viewDidLoad because the view controller being pushed onto the navigation controller is a new instance. 您正在看viewDidLoad,因为被推送到导航控制器的视图控制器是一个实例。 It must call viewDidLoad. 必须调用viewDidLoad。

If you investigate a little further, you would see that each of those view controllers are deallocated when they are popped (just put a breakpoint or NSLog in dealloc). 如果进一步调查,您会看到每个视图控制器在弹出时都被释放(只需在dealloc中放置一个断点或NSLog)。 This deallocation has nothing to do with the view controller container... it does not control the life of the controller it uses... it is just holding a strong reference to it. 这种释放与视图控制器容器无关......它不能控制它使用的控制器的寿命......它只是对它有一个强引用。

When the controller is popped off the navigation controller stack, the nav controller releases its reference, and since there are no other references to it, the view controller will dealloc. 当控制器从导航控制器堆栈弹出时,导航控制器释放其引用,并且由于没有其他引用,视图控制器将解除分配。

The navigation controller only holds strong references to view controllers that are in its active stack. 导航控制器仅保存对其活动堆栈中的视图控制器的强引用。

If you want to reuse the same controller, you are responsible for reusing it. 如果你想重复使用相同的控制器, 负责重新使用。 When you use storyboard segues, you relinquish that control (to a large extent). 当您使用故事板segues时,您放弃该控制(在很大程度上)。

Let's say you have a push segue to view controller Foo as the result of tapping some button. 假设您有一个push segue来查看控制器Foo作为点击某个按钮的结果。 When that button is tapped, "the system" will create an instance of Foo (the destination view controller), and then perform the segue. 当点击该按钮时,“系统”将创建Foo的实例(目标视图控制器),然后执行segue。 The controller container now holds the only strong reference to that view controller. 控制器容器现在拥有该视图控制器的唯一强引用。 Once it's done with it, the VC will dealloc. 一旦完成它,VC将解除分配。

Since it creates a new controller each time, viewDidLoad will be called each time that controller is presented. 由于每次都会创建一个新的控制器,因此每次显示控制器时都会调用viewDidLoad

Now, if you want to change this behavior, and cache the view controller for later reuse, you have to do that specifically. 现在,如果要更改此行为,并缓存视图控制器以供以后重用,则必须专门执行此操作。 If you don't use storyboard segues, it's easy since you are actually pushing/popping the VC to the nav controller. 如果您不使用故事板segue,那么很容易,因为您实际上正在将VC推送/弹出到导航控制器。

If, however, you use storyboard segues, it's a bit more trouble. 但是,如果你使用故事板segues,那就更麻烦了。

There are a number of ways to do it, but all require some form of hacking. 有很多方法可以做到,但都需要某种形式的黑客攻击。 The storyboard itself is in charge of instantiating new view controllers. 故事板本身负责实例化新的视图控制器。 One way is to override instantiateViewControllerWithIdentifier . 一种方法是覆盖instantiateViewControllerWithIdentifier That is the method that gets called when a segue needs to create a view controller. 这是当segue需要创建视图控制器时调用的方法。 It's called even for controllers that you don't give an identifier to (the system provides a made-up unique identifier if you don't assign one). 即使对于没有给出标识符的控制器,也会调用它(如果不指定标识符,系统会提供一个伪造的唯一标识符)。

Note, I hope this is mostly for educational purposes. 请注意,我希望这主要是出于教育目的。 I'm certainly not suggesting this as the best way to resolve your problems, whatever they may be. 我当然不会建议这是解决问题的最佳方法,无论它们是什么。

Something like... 就像是...

@interface MyStoryboard : UIStoryboard
@property BOOL shouldUseCache;
- (void)evict:(NSString*)identifier;
- (void)purge;
@end
@implementation MyStoryboard
- (NSMutableDictionary*)cache {
    static char const kCacheKey[1];
    NSMutableDictionary *cache = objc_getAssociatedObject(self, kCacheKey);
    if (nil == cache) {
        cache = [NSMutableDictionary dictionary];
        objc_setAssociatedObject(self, kCacheKey, cache, OBJC_ASSOCIATION_RETAIN);
    }
    return cache;
}
- (void)evict:(NSString *)identifier {
    [[self cache] removeObjectForKey:identifier];
}
- (void)purge {
    [[self cache] removeAllObjects];
}
- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier {
    if (!self.shouldUseCache) {
        return [super instantiateViewControllerWithIdentifier:identifier];
    }
    NSMutableDictionary *cache = [self cache];
    id result = [cache objectForKey:identifier];
    if (result) return result;
    result = [super instantiateViewControllerWithIdentifier:identifier];
    [cache setObject:result forKey:identifier];
    return result;
}
@end

Now, you have to use this storyboard. 现在,您必须使用此故事板。 Unfortunately, while UIApplication holds onto the main storyboard, it does not expose an API to get it. 不幸的是,虽然UIApplication保留在主故事板上,但它并没有公开API来获取它。 However, each view controller has a method, storyboard to get the storyboard it was created from. 但是,每个视图控制器都有一个方法, storyboard来获取它创建的故事板。

If you are loading your own storyboards, then just instantiate MyStoryboard. 如果您要加载自己的故事板,那么只需实例化MyStoryboard。 If you are using the default storyboard, then you need to force the system to use your special one. 如果您使用的是默认情节提要,那么您需要强制系统使用您的特殊故事板。 Again, there are lots of ways to do this. 同样,有很多方法可以做到这一点。 One simple way is to override the storyboard accessor method in the view controller. 一种简单的方法是覆盖视图控制器中的storyboard访问器方法。

You can make MyStoryboard be a proxy class that forwards everything to UIStoryboard, or you can isa-swizzle the main storyboard, or you can just have your local controller return one from its storyboard method. 您可以将MyStoryboard变成一个代理类,将所有内容转发到UIStoryboard,或者您可以调整主故事板,或者您可以让本地控制器从其storyboard方法中返回一个。

Now, remember, there is a problem here. 现在,请记住,这里有一个问题。 What if you push the same view controller on the stack more than once? 如果您多次在堆栈上推送相同的视图控制器怎么办? With a cache, the exact same view controller object will be used multiple times. 使用缓存,将多次使用完全相同的视图控制器对象。 Is that really what you want? 这真的是你想要的吗?

If not, then you now need to manage interaction with the controller containers themselves so they can check to see if this controller is already known by them, in which case a new instance is necessary. 如果没有,那么您现在需要管理与控制器容器本身的交互,以便他们可以检查它们是否已经知道该控制器,在这种情况下需要新的实例。

So, there is a way to get cached controllers while using default storyboard segues (actually there are quite a few ways)... but that is not necessarily a good thing, and certainly not what you get by default. 因此, 一种方法,而使用默认的故事板塞格斯(实际上有相当多的方式),以获得高速缓存控制器...但是,这并不一定是好事,当然不是你在默认情况下得到的。

It is called every time the controller's view is loaded from scratch (ie requested but not yet available). 每次从头开始加载控制器视图时(即已请求但尚未可用),都会调用它。 If you deallocate the controller and the view goes along with it, then it will be called again the next time you instantiate the controller (for example when you create the controller to push it modally or via segue). 如果取消分配控制器并且视图随之移动,则下次实例化控制器时将再次调用它(例如,当您创建控制器以模态或通过segue推送它时)。 View controllers in tabs are not deallocated because the tab controller keeps them around. 选项卡中的视图控制器未取消分配,因为选项卡控制器会保留它们。

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

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