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

我已经看到堆栈溢出的post很多,指出控制器的viewDidLoad方法只被称为第一次访问控制器,并不一定每次,但总是至less一次。

这不是我所看到的! 我把一个简单的testing放在一起,以突出这个: https : //github.com/imuz/ViewDidLoadTest

这似乎是导航控制器塞格和模态视图总是调用viewDidLoad。 唯一不被调用的时间是在制表符之间切换。

viewDidLoad的每一个解释我都可以发现这个矛盾:

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

苹果自己的文档指出,只有当内存不足时才会卸载视图。

我目前正在viewDidLoad进行初始化,作出每一个segue过渡调用的假设。

我在这里错过了什么?

我相信苹果文档正在描述视图控制器未被释放的情况。 如果你使用segue,那么你正在引起一个新的目的地控制器的实例化,并且作为一个新的对象,它需要加载一个视图。

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

编辑:在阅读你包括的链接,我没有看到他们之间的矛盾。 他们也在讨论在视图控制器对象的生命周期中发生的事情。

菲利普·米尔斯的答案是正确的。 这只是它的一个增强。

系统按照logging工作。

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

如果你进一步调查,你会发现每个视图控制器被取消分配时(只要在dealloc中放置一个断点或NSLog)。 这个释放与视图控制器容器无关…它不控制它使用的控制器的生命…它只是持有一个强有力的参考。

当控制器从导航控制器堆栈popup时,导航控制器释放其引用,并且由于没有其他引用,视图控制器将解除分配。

导航控制器只保存处于活动堆栈中的视图控制器的强引用。

如果你想重复使用同一个控制器, 有责任重用它。 当你使用storyboard时,你放弃了这个控制(很大程度上)。

假设你按了一些button,就可以看到控制器Foo 。 点击该button时,“系统”将创buildFoo (目标视图控制器)的一个实例,然后执行该search。 控制器容器现在拥有对该视图控制器的唯一有力的参考。 一旦完成,VC将会释放。

由于每次都会创build一个新的控制器,每次显示控制器时都会调用viewDidLoad

现在,如果你想改变这种行为,并caching视图控制器以备后用,你必须这样做。 如果你不使用storyboard segues,那很容易,因为你实际上是将VC推入/导航到导航控制器。

但是,如果你使用故事板游戏,那就更麻烦了。

有很多方法可以做到,但都需要某种forms的黑客行为。 故事板本身负责实例化新的视图控制器。 一种方法是覆盖instantiateViewControllerWithIdentifier 。 这是当segue需要创build视图控制器时被调用的方法。 即使对于没有给出标识符的控制器,也会调用它(如果不指定一个标识符,系统会提供一个已编制的唯一标识符)。

请注意,我希望这主要是为了教育目的。 当然,我不认为这是解决问题的最好方法,不pipe它们是什么。

就像是…

 @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来获取它创build的故事板。

如果你正在加载你自己的故事板,那么就实例化MyStoryboard。 如果您正在使用默认的故事板,那么您需要强制系统使用您的特殊故事板。 再次,有很多方法可以做到这一点。 一种简单的方法是覆盖视图控制器中的故事板访问器方法。

您可以使MyStoryboard成为一个将所有内容转发到Uistoryboard的代理类,或者您可以将主要的故事板转换为Isa-Swizzle,也可以让本地控制器从其故事板方法返回一个。

现在请记住,这里有一个问题。 如果你不止一次地在堆栈上推动同一个视图控制器呢? 通过caching,完全相同的视图控制器对象将被多次使用。 那真的是你想要的吗?

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

所以,有一种方法可以在使用默认的storyboard segues的时候获得caching控制器(实际上有很多方法)…但是这不一定是好事,当然也不是默认情况下得到的。

每当从头开始加载控制器的视图(即请求但尚未可用)时调用它。 如果您取消分配控制器并且视图与其一起进行,那么在您下次实例化控制器时(例如,当您创build控制器以通过模式或通过轮播推送控制器时)将再次调用该控制器。 选项卡中的视图控制器不会因为选项卡控制器保留它们而被释放。