【问题标题】:Get specific instance of class from inside super class class method从超类类方法中获取类的特定实例
【发布时间】:2016-12-18 23:33:54
【问题描述】:
+ (NSURLSessionDataTask *)login:(NSString*)email andPassword:(NSString*)password andCallback:(void (^)(NSArray *responseArray, NSError *error))block {


    if(![self hasInternet]){return nil;}
    NSLog(@"Session.login");
    [APIClient sharedClient].requestSerializer = [AFJSONRequestSerializer serializer];
    [[APIClient sharedClient].requestSerializer setValue:email forHTTPHeaderField:@"email"];
    [[APIClient sharedClient].requestSerializer setValue:password forHTTPHeaderField:@"password"];
    [[APIClient sharedClient].requestSerializer setValue:@"poop" forHTTPHeaderField:@"apikey"];


    return [[APIClient sharedClient] POST:@"/login" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON) {
        NSLog(@"session.loginWithEmail.response:%@",JSON);


        if([JSON objectForKey:@"user"]){
            NSMutableDictionary *user=[[NSMutableDictionary alloc] initWithDictionary:[[JSON objectForKey:@"user"] copy]];
            [user setObject:password forKey:@"password"];
            [[Session sharedInstance] startSession:user];
            if([[Session sharedInstance] isSessionActive]){

                if([JSON objectForKey:@"req_onboarding"]){
                    NSLog(@"session.onboard!=nil");
                    [Session sharedInstance].requiredOnboarding=[JSON objectForKey:@"req_onboarding"];
                }

                if (block) {
                    NSLog(@"session.login.block.success");
                    block(nil, nil);
                }
            }else{
                NSLog(@"Failed to set session");
            }


        }

    } failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
        if (block) {

            NSLog(@"Session.login.Fail");
            block([NSArray array], error);
        }
    }];
}

我需要一个可子类化的单例,以便能够有一个抽象的会话管理器来完成大部分工作,但仍然可以进行子类化,以便多个会话可以共存并仍然具有存在的能力在我的应用程序中可用。我正在构建我所有应用程序的一些演示,这就是为什么此功能很重要。

一切都很顺利,直到我意识到托管在超级会话类中的我的 api 方法正在引用单例本身来设置会话,这是一个问题,因为 sharedInstance 被引用如下:

+ (instancetype)sharedInstance
{
    NSLog(@"[Master sharedInstance]");
    id sharedInstance = nil;

    @synchronized(self) {
        NSLog(@"MS | synchronized(self)");

        NSString *instanceClass = NSStringFromClass(self);

        // Looking for existing instance
        sharedInstance = [_sharedInstances objectForKey:instanceClass];

        // If there's no instance – create one and add it to the dictionary
        if (sharedInstance == nil) {
            NSLog(@"MS | sharedInstance == nil");
            sharedInstance = [[super allocWithZone:nil] init];
            [_sharedInstances setObject:sharedInstance forKey:instanceClass];
            NSLog(@"MS | SharedInstances:%@",_sharedInstances);
        }
    }

    return sharedInstance;
}

当它只是一个 Session 单例时,我可以在类方法中这样做:[Session sharedInstance] isSessionActive]

但是现在,[______ sharedInstance] isSessionActive]; 是对调用类方法的特定子类的引用。是否可以从此类方法中检索特定实例的引用而不是将其作为参数发送?

【问题讨论】:

  • +login:andPassword:andCallback: 方法是您自己的代码吗?或者是你不能乱用的代码?如果是您自己的,我建议将其从类方法更改为实例方法,此时您应该能够使用 [self isSessionActive] 如果在 +login:andPassword:andCallback: 上定义了 Session

标签: ios objective-c oop singleton


【解决方案1】:

看起来目标是为超级(可能是抽象)类的每个子类分配一个单例。 sharedInstance 代码并没有完全做到这一点,因为这一行:

sharedInstance = [[super allocWithZone:nil] init];

将只创建超类的实例。我认为您需要子类的实例,以便您可以访问被覆盖的数据和行为。

如果我正确理解您的目标,那么解决方法很简单:

sharedInstance = [[self allocWithZone:nil] init];  // notice "self"

这样,当您将sharedInstance 发送到 ClassA 时,您将获得 ClassA 的(单个)实例。当您将它发送到 ClassB 时,您将获得 ClassB 的(单个)实例。我建议进行更改,这些实际上将是子类 A 和 B 的实例,而不是它们都继承自的类的实例。如果它们都覆盖了 isSessionActive 或任何其他超类方法,调用者将根据他们要求的类单例获得不同的覆盖实现。

【讨论】:

  • 看来问题在于我是如何思考这个问题的,我决定改为将会话管理器设为单例,并且会话不是单例,而是会话管理器直接持有和管理的对象。
  • :-) 最初写这篇文章时,我试图决定是否批评您决定拥有许多“单身人士”的决定,这有点自相矛盾。在我的应用程序中,我的模型类继承自一个知道如何进行网络操作以远程读取和写入它们的类。这会使用并丢弃计数不受控制的会话。
  • 如果 Session 是一个单例,但它有一个 authUser 属性,可以通过 init 传入(具有更多属性的 authUser 的子类)我如何在调用 [Session 时访问子类属性sharedInstance].user.[CUSTOM SSAuthUser property I know exists and is set] from my app?
  • 如果用户属性在标头中公开声明,它应该可以工作。我无法具体评论,因为我不确定您的代码状态,您是否接受了我的建议(这仍然是您问题的正确建议)。
猜你喜欢
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 2015-09-18
  • 2012-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-10
相关资源
最近更新 更多