如何正确管理内存堆栈和视图控制器?

时间:2018-05-03 17:20:06

标签: ios objective-c memory-management viewcontroller

我真的很挣扎于这个基本的iOS编程,但我无法弄清楚发生了什么以及如何解决它。

我有一个主要的Login控制器,用于检测用户何时登录并在auth成功时显示下一个控制器:

@interface LoginViewController (){

    //Main root instance
    RootViewController *mainPlatformRootControler;
}

-(void)loggedInActionWithToken:(NSString *)token anonymous:(BOOL)isAnon{
    NSLog(@"User loged in.");

    mainPlatformRootControler = [self.storyboard instantiateViewControllerWithIdentifier:@"rootViewCOntrollerStoryIdentifier"];

    [self presentViewController:mainPlatformRootControler animated:YES completion:^{

    }];

}

这很有效,没问题。

我的麻烦是处理退出。如何完全删除RootViewController实例并显示一个新实例?

我可以看到RootViewController实例正在堆叠,因为我有多个观察者,在注销后然后登录它们被多次调用(多次我退出并重新输入)。

我尝试过以下方法但没有成功:

首先检测RootViewController中的注销并解除:

[self dismissViewControllerAnimated:YES completion:^{
                [[NSNotificationCenter defaultCenter] postNotificationName:@"shouldLogOut" object:nil];

            }];

然后在LoginViewController中:

-(void)shouldLogOut:(NSNotification *) not{
    NSLog(@"No user signed in");
    mainPlatformRootControler = NULL;
    mainPlatformRootControler = nil;
}

那我怎么办呢?我知道它是一个基本的记忆处理东西,但我不知道怎么做?

7 个答案:

答案 0 :(得分:1)

首先,你必须观察" shouldLogOut"在viewDidLoad中应如下所示:

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(shouldLogout:) name:@"shouldLogout" object:nil];

之后在dismissViewControllerAnimated中应如下所示:

[self dismissViewControllerAnimated:true completion:^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"shouldLogOut" object:nil];
    }];

您需要在登录视图控制器中定义shouldLogOut:selector

-(void)shouldLogOut:(NSNotification *) not{
    mainPlatformRootControler = nil;
}

希望这会对你有帮助!

答案 1 :(得分:0)

问题可能是您在注销发生时永远不会解雇RootViewController。通过将属性mainPlatformRootControler设置为nil,您只需从LoginViewController的角度放弃对象的所有权。这并没有说明任何其他也拥有对mainPlatformRootControler背后的对象的引用。

要解决此问题,请在RootViewController内为退出通知添加通知观察者,并在收到通知后,通过dismiss(animated:completion)

奖金如果你所做的只是保存它,你也不需要属性mainPlatformRootControler。通过适当地解除它(以我上面写的方式),它将自动被清理,因此也不必担心nil它。 (现在,如果你有其他理由保持mainPlatformRootControler,那么不要删除它。

答案 2 :(得分:0)

因为登录和注销是一次性过程,所以登录后,不要只提供新的控制器,而只需用主控制器替换登录控制器。

让我们理解这一点: 您有主窗口的应用程序委托。

didFinishLaunch中的代码:

if (loggedIn) {
     self.window = yourMainController
} else {
     self.window = loginController
}

LoginController中的代码: LoginController将具有AppDelegate的实例,并且在登录后,您必须更改

  

appDelegate.window = mainController

MainController中的代码: MainController将具有AppDelegate的实例,并且在注销后,您必须更改

  

appDelegate.window = loginController

我希望这会有所帮助!!

答案 3 :(得分:0)

您是否在viewDidLoad的{​​{1}}中添加了通知观察者,如下所示

LoginViewController

我猜你错过了这个,然后你的登录课在[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shouldLogOut:) name:@"shouldLogOut" object:nil]; 被解雇后无法收到通知。

答案 4 :(得分:0)

正如你所说,有多个观察者创造了问题,那么当你不需要时,你必须要删除你的观察者。

在RootViewController中

public FileResult DownloadExcel(string filepath) 
{ 
byte[] fileBytes = System.IO.File.ReadAllBytes(filepath); 
   return   File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet,
             `enter code here`Path.GetFileName(filepath)); 
}

------------------------------------------------------------------------

因此,通过这种方式,您不必考虑您的RootViewController是否在堆栈中,或者是从新鲜等处加载。因为实际问题在于您的观察者。

答案 5 :(得分:0)

有许多正确的方法来管理视图层次结构,但我会分享一种我认为简单而有效的方法。

基本上,我在退出/进入时换出主UIWindow的{​​{1}}。此外,我以编程方式提供rootViewController而不是让rootViewController加载初始视图控制器。这样做的好处是,在应用程序启动期间,如果用户已登录,则永远不必加载@UIApplicationMain

可以配置Login.storyboard功能以适应您的风格,但我喜欢交叉溶解过渡,因为它们非常简单。

enter image description here

show

此代码是一个完整的示例,您可以创建一个新项目,清除“主界面”字段,然后将此代码放入应用程序委托中。

结果转换:

enter image description here

答案 6 :(得分:0)

由于您正在解除RootViewController并且在注销后没有引用但是实例未发布,唯一的另一种可能性是其他东西保留对RootViewController的引用。您可能有一个保留周期。 如果两个对象彼此具有强引用,则会发生保留循环。并且因为在释放所有强引用之前无法取消分配对象,所以内存泄漏。

保留周期的例子包括:

>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json; charset=utf8'
>>> r.encoding
'utf-8'
>>> r.text
u'{"type":"User"...'
>>> r.json()
{u'private_gists': 419, u'total_private_repos': 77, ...}

或者

    RootViewController *root = [[RootViewController alloc] init];
    AnOtherViewController *another = [[AnOtherViewController alloc] init];
    //The two instances reference each other
    root.anotherInstance = another;
    another.rootInstance = root; 

解决方案是为其中一个引用使用弱指针。因为弱指针是不保留其目标的指针。 e.g。

    self.block = ^{
                //self is captured strongly by the block
                //and the block is captured strongly by the self instance
                NSLog(@"%@", self);
            };

@property(weak) RootViewController *anotherInstance;