1

我是objective-c的新手,所以请耐心等待这个冗长的解释,我希望它对其他初学者有所帮助。我已经成功地对现有的 iPad 应用程序进行了一些更改。但是,最初的安装/更新例程遇到了未能及时启动的障碍。这里的帖子极大地帮助了我理解问题以及研究的方向。

我已经从这里和其他地方的不同帖子中编译了一个解决方案,因为我没有为初学者找到一个全局的逐行解决方案。

我知道我需要从 didFinishLaunchingWithOptions 中提取数据库初始化/更新,并尽快从这里返回一个实例化的 UIViewController,该 UIViewController 将在主线程之外执行数据库内容(感谢有关该主题的所有海报)。

请注意,如果数据未准备好且完整,则通常在此处调用的 rootVC 无法初始化。因此,仅在 DB 例程上进行异步对我没有帮助,因为 rootVC 会先到达那里,然后在找不到所需的数据时就崩溃了。

即我需要延迟rootVC,而我们需要做任何我们需要做的事情,并且很平静。我选择无缝加载 UILaunchImage 并添加一个微调器。

问题是:

1) 我是否做得正确,这样我就不会再被咬和 8badf00d 了,尤其是在没有增加其他副作用的情况下?或者我应该以其他方式完成它,也许在现有 rootVC 的包装器 init 方法中?

2)dealloc、rootViewController或splashViewController呢?我认为在这个阶段它是相当 rootViewController 。使困惑。

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

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
4

1 回答 1

2

回答我的问题。

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

2) 没有问题,因为我移至 rootVC 初始化包装器解决方案并恢复为解除分配 rootViewController。

3) 问题由 2) 解决。

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

于 2013-04-24T10:20:37.327 回答