Obj-C,初始化阶段,从类方法到实例方法

时间:2014-05-11 10:54:51

标签: objective-c class pointers methods

我正在尝试使用特殊的类方法(XYZperson+(insatncetype)iniPersonDefault:)初始化名为+(instancetype)initWithFirstName...的类的实例(person1,person2),以便使用firstName以及lastNamedateOfBirth(不要担心出生日期是NSString对象,一旦有效,我会改变它。

为方便起见,将这些先前的对象“变量”封装在一个数组中,如下面的代码所示。

我这样做是因为我明白在类方法中处理实例变量是不可能的,所以接口文件中没有@property

.m文件:

#import "XYZPerson.h"

@implementation XYZPerson

// init methods

+(instancetype)initPersonDefault
{
    return [self initWithFirstName:@"John" LastName:@"Doe" DateOfBirth:@"1234"];
}

+(id)initWithFirstName:(NSString *)aFristName
              LastName:(NSString *)aLastName
           DateOfBirth:(NSString *)aDateOfBirth
{
    if (self == [XYZPerson class])
    {
        NSString *firstName2 = aFristName;
        NSString *lastName2 = aLastName;
        NSString *dateOfBirth2 = aDateOfBirth;
        NSArray *info =@[firstName2,lastName2,dateOfBirth2];

        //return  [NSString stringWithFormat:@" %@ %@ born in %@", info[0], info[1], info[2] ];
        //        return (NSString *)lastName2;
        //        return (NSString *)dateOfBirth2;
        return (NSArray *)info;
    }
    return [[self alloc]init];
}

// Other methods

-(void)saysomething : (NSString *)greeting
{
    NSLog(@"%@ ", greeting);
}

-(void)sayHello: (NSArray *)infoP
{
    NSString *results =[[infoP valueForKey:@"description"] componentsJoinedByString:@" "];
    [self saysomething:results];
}

.h文件:

#import <Foundation/Foundation.h>

@interface XYZPerson : NSObject

-(void)saysomething : (NSString *) greeting;

-(void)sayHello: (NSArray *)infoP;

+ (id)initWithFirstName:(NSString *)aFristName
               LastName:(NSString *)aLastName
            DateOfBirth:(NSString *)aDateOfBirth;

+(instancetype)initPersonDefault;

@end

一旦我尝试使用实例方法访问对象变量,我就会感到困惑,这里是main.m

#import <Foundation/Foundation.h>
#import "XYZPerson.h"
#import "XYZShoutingPerson.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        XYZPerson *person1 = [XYZPerson initPersonDefault];
        XYZPerson *person2 = [XYZPerson initWithFirstName:@"Henry" LastName:@"V" DateOfBirth:@"1234"];

        NSLog(@" %@ et %@", [[person1 valueForKey:@"description"] componentsJoinedByString:@" "],
                            [[person2 valueForKey:@"description"] componentsJoinedByString:@" "] );

        [person1 sayHello: person1];
    }
    return 0;
}

这是我的控制台:

  

2014-05-11 12:30:28.258 fonctiontest [4317:303] John Doe 1234 et Henry V 1234
  2014-05-11 12:30:28.278 fonctiontest [4317:303] - [__ NSArrayI sayHello:]:无法识别的选择器发送到实例0x102617a20
  2014-05-11 12:30:28.279 fonctiontest [4317:303] ***由于未捕获的异常'NSInvalidArgumentException'终止应用程序,原因:' - [__ NSArrayI sayHello:]:无法识别的选择器发送到实例0x102617a20'

...

更准确地说,我收到警告说:

  

将'XYZPerson *'发送到'NSArray *'类型参数的不兼容的指针类型

所以我的猜测是person1具有复杂的状态,它是类XYZPerson的一个实例,它还包含一个数组。将它传递给-(void)sayHello : (NSArray *)infoP方法中的指针infoP时,它不起作用。我无法弄清楚如何在person1的数组上调用最后一个方法?任何的想法?

我是初学者所以如果整个逻辑搞砸了,请告诉我如何。

1 个答案:

答案 0 :(得分:2)

您无法使用self从类方法访问实例方法或变量,例如[self saySomething:greeting]在类方法中不起作用。但是,如果该类方法作为参数发送了对象实例,或者它创建了一个新实例,那么您当然可以从类方法访问实例成员。

您似乎希望实现类工厂方法来创建XYZPerson的实例。我建议阅读这些documentation,因为这是Objective-C中一个重要的概念来理解。实际上,我建议现在阅读{{3>}的定义类使用对象部分(最后应该读取整个文档),这样你就可以理解了alloc / init如何工作以及如何正确定义和处理对象。

以下是.h文件的建议类定义,注释为注释:

@interface XYZPerson : NSObject

// 1
+ (instancetype)person;

// 2
- (id)initWithFirstName:(NSString *)aFirstName
               lastName:(NSString *)aLastName // 3
            dateOfBirth:(NSString *)aDateOfBirth;

// 4
@property (copy, nonatomic) NSString *firstName;
@property (copy, nonatomic) NSString *firstName;
@property (copy, nonatomic) NSString *dateOfBirth;

- (void)saysomething: (NSString *) greeting;

// 5
- (void)sayHello;

@end

注意:

  1. initPersonDefault重命名为person,以遵循类工厂方法的命名约定之一。以init开头的方法应该始终是实例方法
  2. 将initWithFirstName ...更改为实例方法(+ to - )
  3. 选择器的固定大写字母
  4. 修改后的init方法将返回XYZPerson的实例,而不是NSArray。它给出的值存储为XYZPerson实例的Programming with Objective-C
  5. 不确定sayHello的实现是什么,但是现在我们已经为name / DOB定义了属性,sayHello方法可以通过self指针访问所有这些数据
  6. 实现:

    @implementation XYZPerson
    
    + (instancetype)person
    {
        // 1
        return [[self alloc] initWithFirstName:@"John"
                                      lastName:@"Doe"
                                   dateOFBirth:@"1234"];
    }
    
    - (id)initWithFirstName:(NSString *)aFirstName
                   lastName:(NSString *)aLastName
                dateOfBirth:(NSString *)aDateOfBirth
    {
        // 2
        if (self = [super init]) {
            // 3
            self.firstName = aFirstName;
            self.lastName = aLastName;
            self.dateOfBirth = aDateOfBirth;
        }
        return self; // 4
    }
    
    - (void)sayHello
    {
        // 5
        NSLog(@"Hello, I am %@ %@", self.firstName, self.LastName);
    }
    
    ...
    @end
    
    1. 类工厂方法通常使用[[self alloc] init...]来创建一个对象,该对象是该类的一个对象,然后根据需要将给定或默认数据传递给init方法以初始化该对象,然后返回该实例。请注意,由于这是一个类方法,self指的是XYZPerson类,而不是XYZPerson对象,因此[self alloc]有点像[XYZPerson alloc](忽略子类的可能性),这就是我们的方式创建一个返回XYZPerson实例
    2. if (self = [super init])检查是一个重要模式,它调用基类中的init方法(此处为NSObject)并确保它不返回nil
    3. 这里我们使用.h文件中定义的属性。我们正在使用正在初始化的XYZPerson实例存储名字/姓氏/ DOB的副本
    4. 返回XYZPerson对象,而不是NSArray
    5. sayHello的示例实施。此方法不再需要参数,因为它可以通过self
    6. 访问名称和DOB属性

      的main.m:

      #import <Foundation/Foundation.h>
      #import "XYZPerson.h"
      #import "XYZShoutingPerson.h"
      
      int main(int argc, const char * argv[])
      {
          @autoreleasepool {
      
              // 1
              XYZPerson *person1 = [XYZPerson person];
              // 2
              XYZPerson *person2 = [[XYZPerson alloc] initWithFirstName:@"Henry" LastName:@"V" DateOfBirth:@"1234"];
      
              // 3
              NSLog(@" %@ et %@", person1.firstName, person1.lastName, person1.dateOfBirth);
      
              // 4
              [person1 sayHello];
          }
          return 0;
      }
      
      1. 此处使用重命名的工厂方法
      2. 使用[[XYZPerson alloc] init...]正确创建XYZPerson实例并使用其初始化程序。请注意此代码与上面+person方法
      3. 的实现之间的相似性
      4. 现在,您可以使用点表示法访问firstName,lastName,dateOfBirth。您将知道它正常工作,因为Xcode将在您键入点
      5. 后自动完成属性名称
      6. sayHello是发送到person1实例的消息。这会调用person1对象上的sayHello方法,这是该方法访问firstName等的方式,而不会在此处传递额外的参数

      7. 关于原始帖子中出现的错误的说明:由于您从该init方法返回NSArray而不是XYZPerson对象,因此在发送{时,会发送无法识别的选择器发送到实例错误{1}}消息发送到sayHello变量,因为person1指向没有sayHello实现的NSArray。