1

更新 1:当 iOS 的数据保护(即密码锁定)关闭时,问题不会发生!

更新 2:禁用 ARC 时不会出现问题。

更新 3:在重新启动 iOS 之前杀死应用程序时不会发生问题。

我的 Settings 类使用 +initialize 实现了单例模式(加上代码以查看发生了什么):

@implementation Settings

static Settings*        sharedSettings;
static NSUserDefaults*  userDefaults;

+ (void)initialize
{
    if ([Settings class] == self)
    {
        sharedSettings = [self new];
        userDefaults = [NSUserDefaults standardUserDefaults];


        // Code to see what's going on here ...
        if (userDefaults == nil)
        {
            [[[UIAlertView alloc] initWithTitle:@"userDefaults == nil"
                                        message:nil
                                       delegate:nil
                              cancelButtonTitle:@"Close"
                              otherButtonTitles:nil] show];
        }
        else
        {
            if ([userDefaults objectForKey:@"Hello"] == nil)
            {
                [[[UIAlertView alloc] initWithTitle:@"Hello == nil"
                                            message:nil
                                           delegate:nil
                                  cancelButtonTitle:@"Close"
                                  otherButtonTitles:nil] show];
            }
            else if ([userDefaults boolForKey:@"Hello"] == NO)
            {
                [[[UIAlertView alloc] initWithTitle:@"Hello == NO"
                                            message:nil
                                           delegate:nil
                                  cancelButtonTitle:@"Close"
                                  otherButtonTitles:nil] show];
            }

            [userDefaults setBool:YES forKey:@"Hello"];
            if ([userDefaults synchronize] == NO)
            {
                [[[UIAlertView alloc] initWithTitle:@"synchronize == NO"
                                            message:nil
                                           delegate:nil
                                  cancelButtonTitle:@"Close"
                                  otherButtonTitles:nil] show];
            }
        }
    }
}

+ (id)allocWithZone:(NSZone*)zone
{
    if (sharedSettings && [Settings class] == self)
    {
        [NSException raise:NSGenericException format:@"Duplicate Settings singleton creation"];
    }

    return [super allocWithZone:zone];
}

+ (Settings*)sharedSettings
{
    return sharedSettings;
}

@end

我在我的 AppDeletate.m 中触发了这段代码(完全剥离):

#import "AppDelegate.h"
#import "Settings.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    [Settings        sharedSettings];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.window makeKeyAndVisible];

    return YES;
}

@end

现在非常奇怪的是,在重新启动 iOS 后立即运行这个空应用程序时,我会同时得到“同步 == NO”和“Hello == nil”弹出窗口。然后当我杀死该应用程序并再次运行它时,一切都很好。事实证明,当我使用 GCD 的 dispatch_after() 延迟 userDefaults = [NSUserDefaults standardUserDefaults] 和后续语句时(我做了 2 秒,但可能同样没问题),问题就消失了。有人可以告诉我为什么会这样,或者这只是一些极端情况下的 iOS 错误或副作用???

补充:事实证明,当我在重新启动 iOS 之前杀死应用程序时,这个问题就消失了。这对我来说证明了它是 iOS 中的东西。

谢谢收听!

科内利斯

ps:你可以相信我,我完全剥离了我的应用程序,只有三个模块:main.mm(标准XCode生成的单行代码)、AppDelegate.m(完整代码如上所示)和Settings.m(完整代码所示)以上)。所以根本没有其他代码在运行。当我进一步剥离它,只留下 AppDelegate.m 时,问题仍然存在。

4

1 回答 1

2

您不应该在+initialize. 没有关于它何时运行以及当时存在哪些其他类的承诺。+initialize应该只做内部对班级的工作。

自添加 GCD 以来,这种单例模式已被取代。您应该出于各种原因使用 GCD 模式,其中之一是它在您认为它运行时运行。

如何实现与 ARC 兼容的 Objective-C 单例?

作为说明,我很好奇你的main.mm. 制作顶级 ObjC++ 几乎总是一个坏主意,并且可能导致一些令人惊讶的副作用。(ObjC++ 是一种古怪的胶水语言,具有许多奇怪的行为。您应该在尽可能少的类中使用它。)我个人会看看是否将其改回以.m解决问题,尽管我仍然推荐 GCD-Singleton 而不是使用+initialize.

于 2013-03-16T14:03:14.613 回答