帮助单身人士

时间:2011-07-22 16:00:14

标签: iphone objective-c ipad singleton

我正在尝试在我的应用中创建一个单例用户类,这里是代码:

#import "User.h"
#import "Login.h"
#import "SFHFKeychainUtils.h"

// Constants
static NSString* const kDBUserCurrentUserIDDefaultsKey = @"kDBUserCurrentUserIDDefaultsKey";

// Current User singleton
static User* currentUser = nil;

@implementation User
@synthesize username = _username;
@synthesize password = _password;
@synthesize delegate = _delegate;


- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }
    return self;
}

+ (NSString*)primaryKeyProperty {
    return @"username";
}

+ (User*)currentUser {
    if (nil == currentUser) {

        id username = [[NSUserDefaults standardUserDefaults] objectForKey:@"kApplicationUserNameKey"];
        if (!username) {
            currentUser = [self new];
        } else{
             NSLog(@"CURRENT USER");
            return self;
        }

        [currentUser retain];
    }

    return currentUser;
}

+ (void)setCurrentUser:(User*)user {
    [user retain];
    [currentUser release];
    currentUser = user;
}

/**
 * Implementation of a RESTful login pattern. We construct an object loader addressed to
 * the /login resource path and POST the credentials. The target of the object loader is
 * set so that the login response gets mapped back into this object, populating the
 * properties according to the mappings declared in elementToPropertyMappings.
 */
- (void)loginWithUsername:(NSString*)username andPassword:(NSString*)password delegate:(NSObject<DBUserAuthenticationDelegate>*)delegate {
    _delegate = delegate;

    //[RKObjectManager sharedManager].client.username = username;
    //[RKObjectManager sharedManager].client.password = password;

    self.username = username;
    self.password = password;

    RKObjectMapping * userMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"LoginViewController"];
    [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/account/verify.json" objectMapping:userMapping delegate:self];
}

/**
* Implementation of a RESTful logout pattern. We POST an object loader to
* the /logout resource path. This destroys the remote session
*/
- (void)logout/*:(NSObject<DBUserAuthenticationDelegate>*)delegate */{  
    NSError * error = nil;
    [[NSUserDefaults standardUserDefaults] setValue:nil forKey:@"kApplicationUserNameKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:@"convore" error:&error];

    NSLog(@"LOGGING OUT");
    if ([self.delegate respondsToSelector:@selector(userDidLogout:)]) {
        [self.delegate userDidLogout:self];
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:@"DBUserDidLogoutNotification" object:nil];
}

- (void)loginWasSuccessful {
    // Upon login, we become the current user
    [User setCurrentUser:self];
    NSError * error = nil;

    // Persist the username for recovery later
    [[NSUserDefaults standardUserDefaults] setValue:self.username forKey:@"kApplicationUserNameKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [SFHFKeychainUtils storeUsername:self.username andPassword:self.password forServiceName:@"convore" updateExisting:TRUE error:&error];

    // Inform the delegate
    if ([self.delegate respondsToSelector:@selector(userDidLogin:)]) {
        [self.delegate userDidLogin:self];
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:@"DBUserDidLoginNotification" object:self];
}

- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response 
{
    NSLog(@"Loaded payload: %@", [response bodyAsString]);
}

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
    if ([objectLoader wasSentToResourcePath:@"/account/verify.json"]) {
        Login * login = (Login *) object;
        if ([login.username length] > 0)
            [self loginWasSuccessful];
    }
}


- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError*)error {  
    if ([objectLoader wasSentToResourcePath:@"/account/verify.json"]) {
         NSLog(@"Encountered an error: %@", error); 
        // Login failed
        if ([self.delegate respondsToSelector:@selector(user:didFailLoginWithError:)]) {
            [self.delegate user:self didFailLoginWithError:error];
        }
    } 
}

- (BOOL)isLoggedIn {
    return self.username != nil;
    //return self.singleAccessToken != nil;
}

- (void)dealloc {
    _delegate = nil;
    [_password release];
    [_passwordConfirmation release];
    [super dealloc];
}

@end

问题在于每当我尝试访问currentUser时,它总是会崩溃。我首先调用了loginWithUsernameandPassword,然后尝试调用currentUser,但是当我在注销时调用currentUser时,它给了我一个错误:

称之为:

if ([[User currentUser] isLoggedIn])

给了我:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[User isLoggedIn]: unrecognized selector sent to class 0x1a671c'

似乎currentUser是零,为什么会这样?

4 个答案:

答案 0 :(得分:4)

Quick Singleton 101(我希望我在开始的时候有这个,大声笑。每个人都只是向我指出那些没有多大帮助的文档)。单身人士的名字将是“Singleton”

//Singleton.h
#import <Foundation/Foundation.h>

@interface SingletonManager : NSObject
{
    NSDictionary* randomDictionary; //just using a dictionary for demonstrative purposes. You can make this a string or whatever you want.
}

+ (Singleton*)sharedSingleton;

@property (nonatomic, retain) NSDictionary *randomDictionary;

@end

现在是.m

//Singleton.m
#import "Singleton.h"

static Singleton *sharedSingleton = nil;

@implementation Singleton

@synthesize randomDictionary;

#pragma mark Singleton Method
+ (Singleton*)sharedSingleton
{
    @synchronized(self)
    {
        if(sharedSingleton == nil)
        {
            sharedSingleton = [[super allocWithZone:NULL] init];
        }
    }
    return sharedSingleton;
}
@end

要设置/获取,首先在您需要的任何类中导入单例:#import "Singleton.h",然后使用Singleton *singletonManager = [Singleton sharedSingleton];获取单例,然后您可以根据需要执行任何操作。即,要获得NSDictionary的描述,您可以调用[[singletonManager randomDictionary] description];

现在这是使用ARC,所以如果你不是,你只需要确保正确管理你的记忆。享受。

答案 1 :(得分:2)

您没有正确编码单身人士。

+ (User *) currentUser {
    @synchronized (self) {

       if (currentUser == nil) {
            currentUser = [[self alloc] init];
       }
       return currentUser;
   }
}

答案 2 :(得分:2)

您需要先获取单例对象,然后才能在其上调用方法。

if ( [[User currentUser] isLoggedIn] ) {
  // Magic happens here
}

答案 3 :(得分:0)

答案实际上是XCodeDev和Matthieu Cormier的两个答案的组合。您需要按照代码示例的方式“保护”您的init,这样就不会创建对象的新版本。否则,它不是真正的单身。 More info on Singleton pattern.

另外,仅仅因为它的单例并不意味着你可以在初始化之后只使用类方法来访问它。您仍然需要获取初始化的实例,否则您无法仅在实例中执行需要特定值的操作。