IOS Singleton класс вылетает мое приложение - PullRequest
0 голосов
/ 27 сентября 2011

У меня проблема с одноэлементным шаблоном.

Я прочитал следующие уроки о синглтон-классах и создал свой собственный.http://www.galloway.me.uk/utorials/singleton-classes/ http://www.johnwordsworth.com/2010/04/iphone-code-snippet-the-singleton-pattern/

При первом создании и запуске приложения оно работает так, как должно.Никаких проблем!Но когда я перестраиваю приложение, синглтон-класс больше не работает должным образом.Первый init работает так, как и должен, но когда я вызываю его снова после нажатия кнопки, происходит сбой моего приложения.

Мой класс синглтона:

BPManager.h

@interface BPManager : NSObject {
    NSString *dbPath;
}

@property (nonatomic, retain) NSString *dbPath;

+ (id)bpManager;
- (void)initDatabase:(NSString *)dbName;
- (int)getQuestions;

@end

BPManager.m

static BPManager *sharedMyManager = nil;

@implementation BPManager

@synthesize dbPath;

- (void)initDatabase:(NSString *)dbName
{   
    dbPath = dbName;
}
-(int)getQuestions
{
    NSLog(@"getQuestions");
}


- (id)init {
    if ((self = [super init])) {
    }
    return self;
}

+ (BPManager *) bpManager {
    @synchronized(self) {

        if(sharedMyManager != nil)  return sharedMyManager;

        static dispatch_once_t pred;        // Lock
        dispatch_once(&pred, ^{             // This code is called at most once per app
            sharedMyManager = [[BPManager alloc] init];
        });
    }

    return sharedMyManager;
}

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

Когда я вызываю следующий код при создании моего интерфейса, приложение создает синглтон:

BPManager *manager = [BPManager bpManager];
[manager initDatabase:@"database.db"];

Примечание. На этом этапе я могу создавать ссылки на классиз других файлов.Но когда я нажимаю на кнопку, он теряет свои ссылки.

Но при нажатии на кнопку следующий код завершается:

BPManager *manager = [BPManager bpManager];
int count = [manager getQuestions];

Приложение должно получить sharedInstance.Это работает, только параметры (например, dbPath) не доступны.Почему это так?

Редактировать:

после некоторых исследований я изменил метод на:

+ (BPManager *) bpManager {
    @synchronized(self) {

        if(sharedMyManager != nil)  return sharedMyManager;

        static dispatch_once_t pred;        // Lock
        dispatch_once(&pred, ^{             // This code is called at most once per app
            sharedMyManager = [[BPManager alloc] init];
        });
    }

    return sharedMyManager;
}

Но проблема не решена

Ответы [ 3 ]

3 голосов
/ 27 сентября 2011

Как насчет

@interface BPManager : NSObject
@property (nonatomic, copy) NSString *dbName;
@property (nonatomic, assign) int questions;
-(id) initWithDBName:(NSString*) dbName {
@end

#import "BPManager.h"
@implementation BPManager
@synthesize dbName=_dbName, questions;
+(BPManager *)singleton {
    static dispatch_once_t pred;
    static BPManager *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[BPManager alloc] initWithDBName:@"database.db"];
    });
    return shared;
}
-(id) initWithDBName:(NSString*) dbName {
    self = [super init]
    if (self) self.dbName = dbName;
    return self;
}
-(void)dealloc {   
    [_dbName release];
    [super dealloc];
}
@end

BPManager *manager = [BPManager singleton];
int count = [manager questions];

Статика является частной для файла реализации, но без причины она должна быть даже доступна вне метода singleton. Init переопределяет реализацию по умолчанию реализацией по умолчанию, поэтому он бесполезен. В Objective-C вы называете метод get именем var (count), а не getCount. Инициализация класса дважды вызывает неопределенное поведение. Нет необходимости синхронизировать или проверять, если == ноль, если вы уже используете dispatch_once, см. Уход и кормление синглетонов . NSString всегда должен использовать copy вместо сохранения в @property. Вам не нужен dealloc, потому что он будет активен всегда, пока ваше приложение работает, но он есть на тот случай, если вы захотите использовать этот класс как не синглтон. И вам, вероятно, так же хорошо, когда этот класс является иваром в вашем делегате, а не одиночным, но вы можете использовать его обоими способами.

0 голосов
/ 19 сентября 2014

Решение Яно должно работать хорошо. Я тоже так использую для создания одноэлементного объекта. И у меня нет никаких проблем.

Для вашего кода, я думаю, что если вы используете @synchronized (это не обязательно, потому что у вас dispatch_once_t, как сказал Яно), вам не следует вызывать return в @ synchronized.

+ (BPManager *) bpManager {
    @synchronized(self) {
        if(sharedMyManager == nil) {
            static dispatch_once_t pred;        // Lock
            dispatch_once(&pred, ^{             // This code is called at most once per app
                sharedMyManager = [[BPManager alloc] init];
            });
        } 
    }

    return sharedMyManager;
}
0 голосов
/ 27 сентября 2011

Я не уверен, является ли это (полным) ответом, но один существенный недостаток заключается в том, что вы используете переменные экземпляра (self, super) в методе класса, +(id)bpManager; Я на самом деле удивлен, что это позволило вам скомпилировать это вообще. Измените @synchronized(self) на @synchronized(sharedMyManager), а [[super alloc...] init] на [[BPManager alloc...] init]. И написание, которое только что заставило меня понять, что проблема выглядит как доступ к подклассовому методу для объекта, созданного как суперкласс, но это должно было быть перезаписано в диспетчере. Разве вам не нужен только один из них в любом случае, почему двойная инициализация такая? (И пока мы там, это утечка памяти - init'd в if() и затем перезаписывается в закрытии, не освобождая его.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...