簡體   English   中英

Objective-C可變子類模式?

[英]Objective-C Mutable subclass pattern?

在Objective-C中是否有用於實現可變/不可變對象類對的標准模式? 我目前有類似以下內容,我根據此鏈接編寫

不可變類:

@interface MyObject : NSObject <NSMutableCopying> {
    NSString *_value;
}

@property (nonatomic, readonly, strong) NSString *value;
- (instancetype)initWithValue:(NSString *)value;

@end

@implementation MyObject
@synthesize value = _value;
- (instancetype)initWithValue:(NSString *)value {
    self = [self init];
    if (self) {
        _value = value;
    }
    return self;
}


- (id)mutableCopyWithZone:(NSZone *)zone {
    return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}

@end

可變類:

@interface MyMutableObject : MyObject
@property (nonatomic, readwrite, strong) NSString *value;
@end


@implementation MyMutableObject
@dynamic value;

- (void)setValue:(NSString *)value {
    _value = value;
}

@end

這有效,但它暴露了iVar。 是否有更好的實施可以解決這種情況?

您的解決方案遵循一個非常好的模式:可變類不會從其基礎復制任何內容,並且在不存儲任何其他狀態的情況下公開其他功能。

這有效,但它暴露了iVar。

由於實例變量默認情況下是@protected ,因此公開的_value僅對繼承MyObject的類可見。 這是一個很好的權衡,因為它可以幫助您避免數據重復,而不會公開暴露用於存儲對象狀態的數據成員。

是否有更好的實施可以解決這種情況?

在類擴展中聲明value屬性。 擴展名類似於沒有名稱的類別,但必須是類實現的一部分。 在MyMutableObject.m文件中,執行以下操作:

@interface MyMutableObject ()
@property(nonatomic, readwrite, strong) value
@end

現在您已經聲明了您的屬性,但它只在您的實現中可見。

dasblinkenlight的答案是正確的。 問題中提供的模式很好。 我提供了兩種不同的替代方案。 首先,以可變類中未使用的iVar為代價,屬性是原子的。 其次,與許多基礎類一樣,不可變實例的副本只返回self。

MyObject.h:

@interface MyObject : NSObject <NSCopying, NSMutableCopying>

@property (atomic, readonly, copy) NSString *value;

- (instancetype)initWithValue:(NSString *)value NS_DESIGNATED_INITIALIZER;

@end

MyObject.m

#import "MyObject.h"
#import "MyMutableObject.h"

@implementation MyObject

- (instancetype)init {
    return [self initWithValue:nil];
}

- (instancetype)initWithValue:(NSString *)value {
    self = [super init];
    if (self) {
        _value = [value copy];
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    // Do not use the iVar here or anywhere else.
    // This pattern requires always using self.value instead of _value (except in the initializer).
    return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}

@end

MyMutableObject.h:

#import "MyObject.h"

@interface MyMutableObject : MyObject

@property (atomic, copy) NSString *value;

@end

MyMutableObject.m:

#import "MyMutableObject.h"

@implementation MyMutableObject

@synthesize value = _value; // This is not the same iVar as in the superclass.

- (instancetype)initWithValue:(NSString *)value {
    // Pass nil in order to not use the iVar in the parent.
    // This is reasonably safe because this method has been declared with NS_DESIGNATED_INITIALIZER.
    self = [super initWithValue:nil];
    if (self) {
        _value = [value copy];
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone {
    // The mutable class really does need to copy, unlike super.
    return [[MyObject allocWithZone:zone] initWithValue:self.value];
}

@end

測試代碼片段:

NSMutableString *string = [NSMutableString stringWithString:@"one"];
MyObject *object = [[MyObject alloc] initWithValue:string];
[string appendString:@" two"];
NSLog(@"object: %@", object.value);
MyObject *other = [object copy];
NSAssert(object == other, @"These should be identical.");
MyMutableObject *mutable1 = [object mutableCopy];
mutable1.value = string;
[string appendString:@" three"];
NSLog(@"object: %@", object.value);
NSLog(@"mutable: %@", mutable1.value);

在上一行之后的一些調試:

2017-12-15 21:51:20.800641-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801423-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801515-0500 MyApp[6855:2709614] mutable: one two
(lldb) po mutable1->_value
one two

(lldb) po ((MyObject *)mutable1)->_value
 nil

正如評論中所提到的,這需要在基類中使用getter而不是iVar。 許多人會認為這是一件好事,但這場辯論在這里是偏離主題的。

您可能會注意到的一個小差異是我已經使用了屬性的copy屬性。 這可以變得強大,而代碼變化很小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM