【问题标题】:Obj-C , initialisation phase, from class method to instance methodObj-C,初始化阶段,从类方法到实例方法
【发布时间】:2014-06-28 19:02:29
【问题描述】:

我正在尝试使用特殊的类方法(+(insatncetype)iniPersonDefault:+(instancetype)initWithFirstName...)初始化名为 XYZperson 的类的实例(person1,person2),以便使用 firstName 和 @ 初始化这些实例987654325@ 和 dateOfBirth (不要担心出生日期在这里是 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 功能测试[4317:303] John Doe 1234 和 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 的数组上调用最后一个方法?任何想法?

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

【问题讨论】:

  • 你永远不会分配你的实例。 init 方法应该是您在未初始化的实例上调用的实例方法,通过向类发送 +alloc 来创建。

标签: objective-c class pointers methods


【解决方案1】:

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

您似乎想实现类工厂方法来创建 XYZPerson 的实例。我建议阅读这些上的documentation,因为它是 Objective-C 中需要理解的一个重要概念。实际上,我建议现在阅读 Programming with Objective-CDefining ClassesWorking with Objects 部分(最终应该阅读整个文档),以便您了解如何分配/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... 更改为实例方法(+ 到 -)
  3. 选择器的固定大小写
  4. 修改后的 init 方法将返回 XYZPerson 的实例,而不是 NSArray。给出的值存储为 XYZPerson 实例的properties
  5. 不确定 sayHello 的实现是什么,但现在我们已经为 name/DOB 定义了属性,sayHello 方法可以通过 self 指针访问所有这些数据

实施:

@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]) 检查是一个重要的模式,它调用基类(这里是 NSObject)中的 init 方法并确保它不返回 nil
  3. 这里我们使用.h 文件中定义的属性。我们正在使用正在初始化的 XYZPerson 实例存储名字/姓氏/DOB 的副本
  4. 返回一个 XYZPerson 对象,而不是 NSArray
  5. sayHello 的示例实现。此方法不再需要参数,因为它可以通过self 访问名称和 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. 现在您可以使用点表示法访问 firstName、lastName、dateOfBirth。您会知道它工作正常,因为 Xcode 会在您键入点后自动完成属性名称
  4. sayHello 是发送到 person1 实例的消息。这会调用 person1 对象上的 sayHello 方法,这就是该方法访问 firstName 等的方式,而无需在此处传递额外的参数

关于您在原始帖子中遇到的错误的说明:由于您从该 init 方法返回一个 NSArray 而不是 XYZPerson 对象,因此在发送 @ 时会收到 unrecognized selector sent to instance 错误987654344@ 消息到person1 变量,因为 person1 指向一个没有 sayHello 实现的 NSArray。

【讨论】:

  • 非常感谢 esker 真的很有帮助,我会仔细重读这两章!
  • 你完全解决了我的问题,非常感谢你的时间和清晰的解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-20
  • 2016-09-20
  • 2011-09-24
  • 2016-06-17
  • 1970-01-01
  • 2017-10-23
  • 2010-10-06
相关资源
最近更新 更多