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



我看到很多关于堆栈溢出的帖子,指出控制器的 viewDidLoad 方法仅在第一次访问控制器时调用,不一定每次都调用,但总是至少一次。

这根本不是我所看到的!我整理了一个简单的测试来强调这一点:https://github.com/imuz/ViewDidLoadTest

似乎对于导航控制器 segue 和模态视图视图总是调用 DidLoad。唯一不调用它的时间是在选项卡之间切换时。

我能找到的对viewDidLoad的每个解释都与此相矛盾:

  • 何时调用 viewDidLoad?
  • UIViewController viewDidLoad vs. viewWillAppear:什么是适当的分工?
  • http://www.manning-sandbox.com/thread.jspa?threadID=41506

苹果自己的文档表明,只有在内存不足时才会卸载视图。

我目前正在 viewDidLoad 中进行初始化,假设每次 segue 转换都会调用它。

我在这里错过了什么吗?

Phillip Mills的答案是正确的。 这只是它的增强。

系统工作正常,记录如下。

您看到的是 viewDidLoad,因为被推送到导航控制器上的视图控制器是一个实例。 它必须调用 viewDidLoad。

如果您进一步调查,您会发现这些视图控制器中的每一个在弹出时都会被释放(只需在 dealloc 中放置断点或 NSLog)。 此释放与视图控制器容器无关...它不控制它使用的控制器的寿命...它只是对它有强烈的引用。

当控制器从导航控制器

堆栈中弹出时,导航控制器会释放其引用,并且由于没有其他引用,因此视图控制器将解除分配。

导航控制器仅包含对其活动堆栈中的视图控制器的强引用。

如果要重用

同一控制器,则负责重用它。 使用情节提要 segue 时,您(在很大程度上)放弃了该控件。

假设您有一个push的续集来查看控制器Foo,因为点击某个按钮。 点击该按钮时,"系统"将创建Foo的实例(目标视图控制器),然后执行 segue。 控制器容器现在保存对该视图控制器的唯一强引用。 一旦完成,VC将解除分配。

由于它每次都会创建一个新控制器,因此每次显示该控制器时都会调用viewDidLoad

现在,如果要更改此行为,并缓存视图控制器以供以后重用,则必须专门执行此操作。 如果不使用情节提要 segue,这很容易,因为您实际上是将 VC 推送/弹出到导航控制器。

但是,如果您使用故事板 segue,那就更麻烦了。

有很多方法可以做到这一点,但都需要某种形式的黑客攻击。 情节提要本身负责实例化新的视图控制器。 一种方法是覆盖instantiateViewControllerWithIdentifier。 这是当 segue 需要创建视图控制器时调用的方法。 即使对于未向其提供标识符的控制器,也会调用它(如果您不分配标识符,系统会提供虚构的唯一标识符)。

请注意,我希望这主要是出于教育目的。 我当然不是建议这是解决你的问题的最佳方式,无论它们是什么。

像...

@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

现在,您必须使用此故事板。 不幸的是,虽然 UIApplication 保留了主情节提要,但它不会公开 API 来获取它。 但是,每个视图控制器都有一个方法,storyboard获取创建它的情节提要。

如果要加载自己的故事板,则只需实例化 MyStoryboard。 如果您使用的是默认情节提要,则需要强制系统使用您的特殊情节提要。 同样,有很多方法可以做到这一点。 一种简单的方法是重写视图控制器中的情节提要访问器方法。

你可以使 MyStoryboard 成为将所有内容转发到 UIStoryboard 的代理类,或者你可以对主情节提要进行 isa 切换,或者你可以让本地控制器从其情节提要方法返回一个。

现在,请记住,这里有一个问题。 如果在堆栈上多次推送相同的视图控制器怎么办? 使用缓存时,将多次使用完全相同的视图控制器对象。 这真的是你想要的吗?

如果没有,那么您现在需要管理与控制器容器本身的交互,以便它们可以检查它们是否已经知道此控制器,在这种情况下,需要一个新实例。

因此,一种方法可以在使用默认情节提要 segue 时获取缓存的控制器(实际上有很多方法)...... 但这不一定是一件好事,当然也不是你默认得到的。

我相信

Apple 文档描述了视图控制器未被解除分配的情况。 如果使用 segue,则会导致新目标控制器的实例化,并且作为新对象,它需要加载视图。

在基于 xib 的应用程序中,我有时会缓存一个控制器对象,我知道我可能会经常重用该对象。 在这些情况下,它们在何时必须加载视图方面与文档保持一致。

编辑:在阅读您包含的链接时,我没有看到它们有任何矛盾。 他们也在谈论视图控制器对象生命周期内发生的事情。

每次从头开始加载控制器的视图(即请求但尚不可用)时都会调用它。 如果解除分配控制器并且视图随之而来,则下次实例化控制器时(例如,当您创建控制器以模式或通过 segue 推送它时),将再次调用它。 选项卡中的视图控制器不会解除分配,因为选项卡控制器会保留它们。

最新更新