-
-
Save victorpimentel/41f2f0ddec132b14606d to your computer and use it in GitHub Desktop.
@interface TMUser1 : NSObject <NSCopying, NSMutableCopying> | |
@property (nonatomic, copy, readonly) NSString *userId; | |
@property (nonatomic, copy, readonly) NSString *name; | |
@property (nonatomic, copy, readonly) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL; | |
@end | |
@interface TMMutableUser1 : TMUser1 | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end |
@interface TMUser1 () | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end | |
@implementation TMUser1 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_userId = [userId copy]; | |
_name = [name copy]; | |
_surname = [surname copy]; | |
_avatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (TMUser1 *)copyWithZone:(NSZone *)zone | |
{ | |
return self; | |
} | |
- (TMMutableUser1 *)mutableCopyWithZone:(NSZone *)zone | |
{ | |
return [[TMMutableUser1 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
@end | |
@implementation TMMutableUser1 | |
@dynamic userId; | |
@dynamic name; | |
@dynamic surname; | |
@dynamic avatarURL; | |
- (TMUser1 *)copyWithZone:(NSZone *)zone | |
{ | |
return [[TMUser1 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
@end |
@interface TMUser2 : NSObject <NSCopying, NSMutableCopying> | |
@property (nonatomic, copy, readonly) NSString *userId; | |
@property (nonatomic, copy, readonly) NSString *name; | |
@property (nonatomic, copy, readonly) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL; | |
@end | |
@protocol TMMutableUser2 <NSObject> | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end | |
typedef TMUser2<TMMutableUser2> TMMutableUser2; |
@interface TMUser2 () | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end | |
@implementation TMUser2 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_userId = [userId copy]; | |
_name = [name copy]; | |
_surname = [surname copy]; | |
_avatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (TMUser2 *)copyWithZone:(NSZone *)zone | |
{ | |
return [[TMUser2 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
- (TMMutableUser2 *)mutableCopyWithZone:(NSZone *)zone | |
{ | |
return [self copyWithZone:zone]; | |
} | |
@end |
@interface TMUser3 : NSObject <NSCopying, NSMutableCopying> | |
@property (nonatomic, copy, readonly) NSString *userId; | |
@property (nonatomic, copy, readonly) NSString *name; | |
@property (nonatomic, copy, readonly) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL; | |
@end | |
@interface TMMutableUser3 : TMUser3 | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end |
@implementation TMUser3 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_userId = [userId copy]; | |
_name = [name copy]; | |
_surname = [surname copy]; | |
_avatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (TMUser3 *)copyWithZone:(NSZone *)zone | |
{ | |
return self; | |
} | |
- (TMMutableUser3 *)mutableCopyWithZone:(NSZone *)zone | |
{ | |
return [[TMMutableUser3 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
@end | |
@interface TMMutableUser3 () | |
@property (nonatomic, copy) NSString *mutableUserId; | |
@property (nonatomic, copy) NSString *mutableName; | |
@property (nonatomic, copy) NSString *mutableSurname; | |
@property (nonatomic, copy) NSURL *mutableAvatarURL; | |
@end | |
@implementation TMMutableUser3 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_mutableUserId = [userId copy]; | |
_mutableName = [name copy]; | |
_mutableSurname = [surname copy]; | |
_mutableAvatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (TMUser1 *)copyWithZone:(NSZone *)zone | |
{ | |
return [[TMUser1 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
- (NSString *)userId | |
{ | |
return self.mutableUserId; | |
} | |
- (void)setUserId:(NSString *)userId | |
{ | |
self.mutableUserId = userId; | |
} | |
- (NSString *)name | |
{ | |
return self.mutableName; | |
} | |
- (void)setName:(NSString *)name | |
{ | |
self.mutableName = name; | |
} | |
- (NSString *)surname | |
{ | |
return self.mutableSurname; | |
} | |
- (void)setSurname:(NSString *)surname | |
{ | |
self.mutableSurname = surname; | |
} | |
- (NSURL *)avatarURL | |
{ | |
return self.mutableAvatarURL; | |
} | |
- (void)setAvatarURL:(NSURL *)avatarURL | |
{ | |
self.mutableAvatarURL = avatarURL; | |
} | |
@end |
@interface TMUser4 : NSObject <NSCopying, NSMutableCopying> | |
@property (nonatomic, copy, readonly) NSString *userId; | |
@property (nonatomic, copy, readonly) NSString *name; | |
@property (nonatomic, copy, readonly) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL; | |
@end | |
@interface TMMutableUser4 : TMUser4 | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end |
@interface TMUser4 () | |
{ | |
@protected | |
NSString *_userId; | |
NSString *_name; | |
NSString *_surname; | |
NSURL *_avatarURL; | |
} | |
@end | |
@implementation TMUser4 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_userId = [userId copy]; | |
_name = [name copy]; | |
_surname = [surname copy]; | |
_avatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (TMUser4 *)copyWithZone:(NSZone *)zone | |
{ | |
return self; | |
} | |
- (TMMutableUser4 *)mutableCopyWithZone:(NSZone *)zone | |
{ | |
return [[TMMutableUser4 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
@end | |
@implementation TMMutableUser4 | |
@dynamic userId; | |
@dynamic name; | |
@dynamic surname; | |
@dynamic avatarURL; | |
- (TMUser4 *)copyWithZone:(NSZone *)zone | |
{ | |
return [[TMUser4 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
- (void)setUserId:(NSString *)userId | |
{ | |
_userId = [userId copy]; | |
} | |
- (void)setName:(NSString *)name | |
{ | |
_name = [name copy]; | |
} | |
- (void)setSurname:(NSString *)surname | |
{ | |
_surname = [surname copy]; | |
} | |
- (void)setAvatarURL:(NSURL *)avatarURL | |
{ | |
_avatarURL = avatarURL; | |
} | |
@end |
@protocol TMUser5 <NSCopying, NSMutableCopying> | |
@property (nonatomic, copy, readonly) NSString *userId; | |
@property (nonatomic, copy, readonly) NSString *name; | |
@property (nonatomic, copy, readonly) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
@end | |
@interface TMMutableUser5 : NSObject <TMUser5> | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL; | |
@end | |
typedef NSObject<TMUser5> TMUser5; |
@implementation TMMutableUser5 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_userId = [userId copy]; | |
_name = [name copy]; | |
_surname = [surname copy]; | |
_avatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (id<TMUser5>)copyWithZone:(NSZone *)zone | |
{ | |
return [[TMMutableUser5 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
- (TMMutableUser5 *)mutableCopyWithZone:(NSZone *)zone | |
{ | |
return [self copyWithZone:zone]; | |
} | |
@end |
@interface TMUser6 (Mutability) | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end |
@interface TMUser6 : NSObject <NSCopying, NSMutableCopying> | |
@property (nonatomic, copy, readonly) NSString *userId; | |
@property (nonatomic, copy, readonly) NSString *name; | |
@property (nonatomic, copy, readonly) NSString *surname; | |
@property (nonatomic, readonly) NSURL *avatarURL; | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL; | |
@end |
@interface TMUser6 () | |
@property (nonatomic, copy) NSString *userId; | |
@property (nonatomic, copy) NSString *name; | |
@property (nonatomic, copy) NSString *surname; | |
@property (nonatomic) NSURL *avatarURL; | |
@end | |
@implementation TMUser6 | |
- (instancetype)initWithUserId:(NSString *)userId | |
name:(NSString *)name | |
surname:(NSString *)surname | |
avatarURL:(NSURL *)avatarURL | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_userId = [userId copy]; | |
_name = [name copy]; | |
_surname = [surname copy]; | |
_avatarURL = avatarURL; | |
} | |
return self; | |
} | |
- (TMUser6 *)copyWithZone:(NSZone *)zone | |
{ | |
return [[TMUser6 alloc] initWithUserId:self.userId | |
name:self.name | |
surname:self.surname | |
avatarURL:self.avatarURL]; | |
} | |
- (TMUser6 *)mutableCopyWithZone:(NSZone *)zone | |
{ | |
return [self copyWithZone:zone]; | |
} | |
@end |
There is one more solution not listed here, which is not having mutable objects at all.
@interface TMUser1 : NSObject
@property (nonatomic, copy, readonly) NSString *userId;
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, copy, readonly) NSString *surname;
@property (nonatomic, readonly) NSURL *avatarURL;
- (instancetype)initWithUserId:(NSString *)userId
name:(NSString *)name
surname:(NSString *)surname
avatarURL:(NSURL *)avatarURL;
- (instancetype)userByMutatingUserId:(NSString *)userId;
- (instancetype)userByMutatingName:(NSString *) name;
- (instancetype)userByMutatingSurname:(NSString *) surname;
- (instancetype)userByMutatingAvatarURL:(NSURL *) avatarURL;
@end
@implementation TMUser6
- (instancetype)initWithUserId:(NSString *)userId
name:(NSString *)name
surname:(NSString *)surname
avatarURL:(NSURL *)avatarURL
{
self = [super init];
if (self)
{
_userId = [userId copy];
_name = [name copy];
_surname = [surname copy];
_avatarURL = avatarURL;
}
return self;
}
- (instancetype)userByMutatingUserId:(NSString *)userId
{
return [[[self class] alloc] initWithUserId:userId
name:self.name
surname:self.surname
avatarURL:self.avatarURL];
}
- (instancetype)userByMutatingName:(NSString *)name
{
return [[[self class] alloc] initWithUserId:self.userId
name:name
surname:self.surname
avatarURL:self.avatarURL];
}
- (instancetype)userByMutatingSurname:(NSString *)surname
{
return [[[self class] alloc] initWithUserId:self.userId
name:self.name
surname:surname
avatarURL:self.avatarURL];
}
- (instancetype)userByMutatingAvatarURL:(NSURL *)avatarURL
{
return [[[self class] alloc] initWithUserId:self.userId
name:self.name
surname:self.surname
avatarURL:avatarURL];
}
@end
How this approach ranks in your categories:
- Verbosity: 68 lines, very verbose, but not the worst option.
- Safety ranking: Safe, there is no way to mutate an object, which was the whole point of this!
- Efficiency: Always require a copy. Not efficient compared with other approaches.
- "Prone to errors": Very safe, really difficult to screw up IMHO (other than really stupid copy&pasting errors, trivial to detect and fix).
- Usage ranking: Same usage we already know from many Cocoa casses (see for example
NSURL
and its methods familyURLByAppendingPathComponent:
). - Implementation weirdness ranking: I don't see any weirdness in this approach, just objects.
- Testing ranking: In tests you probably would like to write Categories if you need to mutate objects very often... But you can just create them and threat them as immutable objects, which is precisely what they are.
This option could be painful for big objects, but none of the others is free from writting stupid boilerplate. And with this approach we have real proper immutable objects, instead of 'workarounds'.
My preference order would be this option first, and if not, then the option #2, which is very weird, and hacky, and murky, but at least is the easiest and shortest. And when it comes to mutable vs. immutable, there is nothing in between. There is nothing like 'half mutable'. Every approach in the list is making objects mutable in one way or another. So if we choose to mutate, let's mutate with the simplest possible approach.
Yeah, @cesteban, I wrote the ones that kept the NSCopying/NSMutableCopying interface, because I thought that we already voted for it in the arch committee after testing it in the field.
However we can decide it again if we see that other option with a different API will be better for us. That can open a can of worms too :P because then we have another option: to just publish the initialize and that's just it. TMPhoneNumber follows that option, and it has resulted in an explosion of initializers :/
With all these versions you can do this (verbatim, even with the ones that use protocols):
Verbosity ranking (Lines of code in .m)
Safety ranking
Efficiency ranking
"Prone to errors" ranking (kind of subjective)
Usage ranking
Implementation weirdness ranking (hard to understand)
Testing ranking