简体   繁体   English

子类中的Objective-C只读属性

[英]Objective-C readonly properties in a subclass

I have a superclass called SuperClass a read-only property. 我有一个称为SuperClass的超类,它是一个只读属性。 That looks like this: 看起来像这样:

@property (nonatomic, strong, readonly) NSArray *arrayProperty;

In a subclass I need an initializer that takes a instance of SuperClass as a parameter: 在子类中,我需要一个初始化程序,该初始化程序将SuperClass的实例作为参数:

- (instancetype)initWithSuperClass:(SuperClass *)superClass

I created a GitHub sample project that shows what the problem is: https://github.com/marosoaie/Objc-test-project 我创建了一个GitHub示例项目来显示问题所在: https//github.com/marosoaie/Objc-test-project

I cannot do _arrayProperty = superClass.arrayProperty in the initializer. 我无法在初始化程序中执行_arrayProperty = superClass.arrayProperty I want to keep the property read-only in SubClass as well. 我也想在SubClass中保持该属性为只读。

Any ideas on how this could be solved? 关于如何解决的任何想法?

I know I could declare the property as readwrite in a class extension inside the SubClass implementation file, but I'm hoping that there's a better solutions than this. 我知道我可以在SubClass实现文件中的类扩展中将该属性声明为readwrite,但是我希望有比这更好的解决方案。

Edit: SuperClass.h 编辑:SuperClass.h

#import <Foundation/Foundation.h>

@interface SuperClass : NSObject
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;

@property (nonatomic, strong, readonly) NSString *stringProperty;
@property (nonatomic, strong, readonly) NSArray *arrayProperty;

@end

SuperClass.m 超类

#import "SuperClass.h"

@implementation SuperClass

- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
    self = [super init];
    if (self) {
        _arrayProperty = dictionary[@"array"];
        _stringProperty = dictionary[@"string"];
    }
    return self;
}

@end

SubClass.h: SubClass.h:

#import <Foundation/Foundation.h>
#import "SuperClass.h"

@interface SubClass : SuperClass

@property (nonatomic, strong, readonly) NSString *additionalStringProperty;
- (instancetype)initWithSuperClass:(SuperClass *)superClass;

@end

SubClass.m: 子类.m:

#import "SubClass.h"

@implementation SubClass
@synthesize additionalStringProperty = _additionalStringProperty;

- (NSString *)additionalStringProperty
{
    if (!_additionalStringProperty) {
        NSMutableString *mutableString = [[NSMutableString alloc] init];

        for (NSString *string in self.arrayProperty) {
            [mutableString appendString:string];
        }

        _additionalStringProperty = [mutableString copy];
    }
    return _additionalStringProperty;
}

- (instancetype)initWithSuperClass:(SuperClass *)superClass
{
    self = [super init];
    if (self) {
//        Doesn't work
//        _stringProperty = superClass.stringProperty;
//        _arrayProperty = superClass.arrayProperty;

    }
    return self;
}



@end

You already exposed an initializer, that writes to that readonly property -initWithDictionary: . 您已经暴露了一个初始化程序,该初始化程序写入该只读属性-initWithDictionary: Call that in your SubClass, instead [super init] : 在您的SubClass中调用它,而不是[super init]

- (instancetype)initWithSuperClass:(SuperClass *)superClass {
    NSDictionary *dict = @{
        @"array": superClass.arrayProperty,
        @"string": superClass.stringProperty,
    };
    self = [super initWithDictionary:dict];
    if (self) {
        // Nothing here.
    }
    return self;
}

It's quite common to have an initializer for readonly properties, although using dictionary is not that good solution. 对于只读属性有一个初始化器是很常见的,尽管使用字典并不是一个好的解决方案。 Typically, I would create: 通常,我会创建:

- (instancetype)initWithArray:(NSArray *)array string:(NSString *)string;

First of all, there is a bug in your test setup: Your key in - (instancetype)initWithDictionary:(NSDictionary *)dictionary is @"array" , where the array contains @"arrayProperty" . 首先,测试设置中存在一个错误@"arrayProperty" - (instancetype)initWithDictionary:(NSDictionary *)dictionary@"array" ,其中数组包含@"arrayProperty"

Regarding your problem: 关于您的问题:

//...
@interface SuperClass : NSObject
{
    @protected // this is what you want: a protected class property, accessible in subclasses, but no where else
    NSString *_stringProperty;
    NSArray *_arrayProperty;
}

@property (nonatomic, strong, readonly) NSString *stringProperty;
@property (nonatomic, strong, readonly) NSArray *arrayProperty;

- (instancetype)initWithDictionary:(NSDictionary *)dictionary;

@end


// SubClass.m
//...
@implementation SuperClass

- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
    self = [super init];
    if (self) {
        _arrayProperty = dictionary[@"arrayProperty"]; // this was @"array", so could not work
        _stringProperty = dictionary[@"stringProperty"]; // same here
    }
    return self;
}

@end

Then it works. 然后就可以了。 In addition, I would write 另外,我会写

@interface SubClass ()

@property (nonatomic, strong, readwrite) NSString *additionalStringProperty;

@end


@implementation SubClass

- (instancetype)initWithSuperClass:(SuperClass *)superClass
{
    self = [super init];
    if (self) {
        _stringProperty = superClass.stringProperty;
        _arrayProperty = superClass.arrayProperty;
    }
    return self;
}

because I prefer the readwrite property in a class extension over the @synthesize magic. 因为相比@synthesize魔术,我更喜欢类扩展中的readwrite属性。 But this is a personal opinion. 但这是个人观点。

One main issue regarding to class design still holds: What happens if (similar to your test setup) the dictionary of the superclass does not contain the key? 有关类设计的一个主要问题仍然存在:如果(类似于您的测试设置)超类的字典不包含键,将会发生什么? Then it won't be initialized, which is not a good idea, because you expect them to be initialized. 然后它不会被初始化,这不是一个好主意,因为您希望它们被初始化。 So you should check in the subclass if superclass.stringProperty is not nil and add a standard constructor for the superclass to avoid that the two dictionaries are uninitialized. 因此,如果superclass.stringProperty不为nil,则应检查子类,并为超类添加标准构造函数,以避免两个字典未初始化。

In your SuperClass.m: 在您的SuperClass.m中:

- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
    self = [super init];
    if (self) {
       // these were always nil, check your dictionary keys
       _arrayProperty = dictionary[@"arrayProperty"];
       _stringProperty = dictionary[@"stringProperty"];
    }
    return self;
}

In your SubClass.m: 在您的SubClass.m中:

 @interface SubClass ()

 @property (strong, nonatomic) NSString * additionalStringProperty;

 @property (strong, nonatomic) NSString * subClassString;
 @property (strong, nonatomic) NSArray * subClassArray;

 @end

@implementation SubClass
- (instancetype)initWithSuperClass:(SuperClass *)superClass
{
    self = [super init];
    if (self) {
       _subClassString = superClass.stringProperty;
       _subClassArray  = superClass.arrayProperty;
    }
return self;
}

I tried the answers here to no avail. 我在这里尝试答案无济于事。 What ended up working for me was this answer which mentions that you should directly access the member variable (after declaring it as protected) like so: 最终对我有用的是这个答案 ,它提到您应该直接访问成员变量(在将其声明为受保护后),如下所示:

self->_stringProperty = @"some string";

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM