Permalink
Browse files

Update the API to use both class method and instance method.

  • Loading branch information...
1 parent cc3dfed commit 2c9e67ea69629916bb12d92a02907289446121cc @zhigang1992 committed Jun 8, 2013
@@ -41,5 +41,6 @@ SORelativeDateTransformer is a value transformer that generates a human-readable
\return An NSString with the generated and localized phrase.
*/
- (id) transformedValue:(id)value;
++ (id) transformedValue:(id)value;
@end
@@ -13,6 +13,8 @@
#define __has_feature(x) 0
#endif
+static SORelativeDateTransformer *_sharedInstance;
+
@implementation SORelativeDateTransformer
+ (NSBundle *)bundle {
@@ -148,5 +150,16 @@ - (id) transformedValue:(id)value
return transformedValue;
}
++ (SORelativeDateTransformer *)shardTransformer{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _sharedInstance = [[self alloc] init];
+ });
+ return _sharedInstance;
+}
+
++ (id)transformedValue:(id)value{
+ return [[self shardTransformer] transformedValue:value];
+}
@end

2 comments on commit 2c9e67e

Zhigang, thanks for this work. It is a compact way to get a transformed value.

My only concern is that your approach does ignore the capabilities already built into NSValueTransformer.

In particular, NSValueTransformer maintains an internal cache of all registered value transformers. The +setValueTransformer:forName: method lets you explicitly register a single instance of a given transformer that can then be used throughout an application.

I had given some thought to making a convenience class method such as you've created here, but decided against it. It is pretty easy to use the existing NSValueTransformer API to implement what you want to do.

For example:

+ (id) transformedValue:(id)value
{
    SORelativeDateTransformer *registered;
    registered = [NSValueTransformer valueTransformerForName:@"SORelativeDateTransformer"];
    return [registered transformedValue:value];
}

The NSValueTransformer class method +valueTransformerForName: looks for a registered transformer with the given name. If it doesn't find an already registered instance, it will create one and cache it. The next invocation of +valueTransformerForName: would then return the now-cached instance.

I believe it would be a better API design for me to add a +registeredTransformer class method to SORelativeDateTransformer that wraps the NSValueTransformer API into something moderately less lengthy to type.

+ (id) registeredTransformer
{
    return [NSValueTransformer valueTransformerForName:NSStringFromClass(self)];
}

From anywhere in an application, then, a transformed date value could be obtained using something like

NSDate *someDate = ...;
NSString *dateText = [[SORelativeDataTransformer registeredTransformer] transformedValue:someDate];

The NSValueTransformer API is already verbose and the name "SORelativeDateTransformer" doesn't do much to make it less so. A +registeredTransformer class method might help a little.

Alternately, you could define a global inline function for use in your app:

inline NSString *RelativeDateStringFromDate(NSDate *date)
{
    return [[NSValueTransformer valueTransformerForName:@"SORelativeDateTransformer"] transformedValue:date];
}

or a macro

#define RelativeDateStringFromDate(date) [[NSValueTransformer valueTransformerForName:@"SORelativeDateTransformer"] transformedValue:date]

Your approach of explicitly creating a static instance is fine, but does ignore NSValueTransformer's existing caching feature. I'd rather keep the class as simple as possible and not duplicate existing functionality. I'm going to reject this pull request, but let me know if you think the +registeredTransformer method would be a reasonable substitute for use in your app. If so, I'll put that in.

Thanks again,

Bill

Owner

zhigang1992 replied Jun 12, 2013

Please sign in to comment.