我是Objective-c的新手,请耐心等待这个冗长的解释,希望对其他初学者有所帮助。我已经成功地对现有iPad应用程序进行了一些更改。但是,原始的安装/更新例程已无法及时启动。此处的帖子极大地帮助了我了解了问题以及研究的方向。

由于找不到针对初学者的逐行全局解决方案,因此我在这里和其他地方的不同文章中都编写了解决方案。

我了解我需要从didFinishLaunchingWithOptions中拉出数据库init / update,并从此处以实例化的UIViewController尽快返回,该UIViewController将使数据库脱离主线程(感谢主题的所有发布者)。

请注意,如果数据尚未准备好且完好无损,则通常在此处调用的rootVC无法初始化。因此,仅对DB例程执行异步操作无济于事,因为rootVC首先到达那里,并在找不到所需数据时爆炸。

即我需要延迟rootVC,同时我们需要做和平地做任何我们需要做的。我选择加载UILaunchImage以使其无缝,并添加一个微调器。

问题是:

1)我是否已正确完成操作,以使我再也不会被咬伤和8badf00d了,尤其是不增加其他副作用的情况下?还是应该以其他方式进行操作,也许是在现有rootVC的包装器init方法中?

2)那dealloc,rootViewController或splashViewController呢?我认为到这个阶段它是rootViewController。困惑。

3)它可以工作,但是这真的是由rootViewController作为rootVC替换(并删除)splashViewController吗?还是我把它们堆起来...

之前

RAppDelegate.h

#import <UIKit/UIKit.h>
#import "RDataManager.h"
#import "RRootViewController.h"
#import "RScreenViewController.h"

@interface RAppDelegate : NSObject <UIApplicationDelegate>
{
    UIWindow *window;
    RRootViewController *rootViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, readonly) RRootViewController *rootViewController;

@end

RAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[RDataManager sharedManager] updateDatabase];  // This is what takes time...
    rootViewController = [[RRootViewController alloc] initAtScreen:kScreenTypeIndex withCar:carId];
    rootViewController.wantsFullScreenLayout = YES;
    self.window.rootViewController = rootViewController;
    [self.window makeKeyAndVisible];

    return YES;
}
...
- (void)dealloc
{
   [rootViewController.view removeFromSuperview];
   [rootViewController release];
   [window release];
   [super dealloc];
}

之后

RAppDelegate.h
#import <UIKit/UIKit.h>
#import "RDataManager.h"
#import "RScreenViewController.h"
#import "RSplashViewController.h"

@interface RAppDelegate : NSObject <UIApplicationDelegate>
{
    UIWindow *window;
    RSplashViewController *splashViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, readonly) RSplashViewController *splashViewController;

@end

RAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    splashViewController = [[RSplashViewController alloc] init];
    splashViewController.wantsFullScreenLayout = YES;
    self.window.rootViewController = splashViewController;
    [self.window makeKeyAndVisible];

    return YES;
}
...
- (void)dealloc
{
   [splashViewController.view removeFromSuperview];
   [splashViewController release];
   [window release];
   [super dealloc];
}

RSplashViewController.h
#import <UIKit/UIKit.h>
#import "RRootViewController.h"

@interface RSplashViewController : UIViewController
{
    UIImageView *splashImageView;
    RRootViewController *rootViewController;
    UIActivityIndicatorView *spinner;
}

@property (nonatomic, retain) UIImageView *splashImageView;
@property (nonatomic, readonly) RRootViewController *rootViewController;

@end

RSplashViewController.m
#import "RSplashViewController.h"
#import "RDataManager.h"

@interface RSplashViewController ()

@end

@implementation RSplashViewController

@synthesize splashImageView;
@synthesize rootViewController;

- (void) loadView
{
    CGRect appFrame = [UIInterface frame];
    UIView *view = [[UIView alloc] initWithFrame:appFrame];
    self.view = view;
    [view release];

    NSString *splashFile = [[NSBundle mainBundle] pathForResource:@"LaunchImage-jaguar-Landscape~ipad" ofType:@"png"];
    UIImage *splashImage = [[UIImage alloc] initWithContentsOfFile:splashFile];
    splashImageView = [[UIImageView alloc] initWithImage:splashImage];
    [self.view addSubview:splashImageView];
    [splashImage release];

    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    CGRect frame = spinner.frame;
    frame.origin.x = CGRectGetMidX(self.view.frame) - CGRectGetWidth(spinner.frame) / 2;
    frame.origin.y = 650;
    spinner.frame = frame;
    spinner.hidesWhenStopped = YES;
    [self.view addSubview:spinner];
    [spinner startAnimating];
}

- (void) viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // how we stop DB refresh from freezing the main UI thread
    dispatch_queue_t updateQueue = dispatch_queue_create("updateDB", NULL);
    dispatch_async(updateQueue, ^{

        // do our long running process here
        [[RDataManager sharedManager] updateDatabase];

        // do any UI stuff on the main UI thread
        dispatch_async(dispatch_get_main_queue(), ^{
            [spinner stopAnimating];
            [splashImageView removeFromSuperview];
            rootViewController = [[RRootViewController alloc] initAtScreen:kScreenTypeGarage withCar:nil needSplashScreen:YES];
            [self.view addSubview:rootViewController.view];
        });

    });
    dispatch_release(updateQueue);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void) dealloc
{
    [super dealloc];
}

@end

最佳答案

回答我的问题。

1)是的,使用SplashViewController或rootVC初始化包装器,这是安全的。临时运行2分钟没有问题。仔细检查更新是否确实在单独的线程中运行(Xcode调试窗口窗格)。

2)自从我移到rootVC初始化包装器解决方案并恢复为取消分配rootViewController以来,没有问题。

3)2)解决的问题。

我还设法将更新时间减少了90%,无论如何这都是理想的解决方案,但是很高兴知道我也安全地摆脱了看门狗计时器的困扰。

08-25 21:41