1

I'm trying to convert some Objective-C code to Swift. I've found some similar questions: Swift equivalent to Objective-C Synchronized and objc_sync_enter / objc_sync_exit not working with DISPATCH_QUEUE_PRIORITY_LOW, but I'm not completely happy with the answers. Many answers suggest objc_sync_exit and objc_sync_enter, which as far as I can tell, are undocumented implementation details of Apple's frameworks, so I'm not comfortable using them in my production app.

The specific code I'm trying to convert involves an NSDateFormatter which can be accessed by multiple methods:

@implementation NSDateFormatter (MyApp)
+ (NSDateFormatter *)MyAppDateFormatter
{
    static NSDateFormatter *formatter = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        formatter = [[NSDateFormatter alloc] init];
    });
    return formatter;
}

+ (NSString *)stringFromDate:(NSDate *)date withDateFormat:(NSString *)dateFormat
{
    NSDateFormatter *formatter = [NSDateFormatter MyAppDateFormatter];
    NSString *dateString = nil;

    @synchronized(formatter)
    {
        formatter.dateFormat = dateFormat;

        dateString = [formatter stringFromDate:date];
    }

    return dateString;
}

+ (NSDate *)dateFromString:(NSString *)string
{
    NSDateFormatter *formatter = [NSDateFormatter MyAppDateFormatter];

    NSDate *date = nil;

    @synchronized(formatter)
    {
        formatter.dateStyle = NSDateFormatterFullStyle;
        date = [formatter dateFromString:string];
    }

    return date;
}
@end

In my swift code, I made the MyAppDateFormatter a static constant, which in Swift is automatically lazily initialized.

extension DateFormatter {
    private static let myAppDateFormatter = DateFormatter()

    static func string(from date: Date, with dateFormat: String) -> String {
        let dateFormatter = DateFormatter.myAppDateFormatter
        dateFormatter.dateFormat = dateFormat

        return dateFormatter.string(from: date)
    }

    static func date(from string: String) -> Date? {
        let dateFormatter = DateFormatter.myAppDateFormatter
        dateFormatter.dateStyle = .full

        return dateFormatter.date(from: string)
    }
}

How do I reproduce the @synchronized behavior in Swift? Would adding getters and setters with GCD functions to myAppDateFormatter, as seen here work? And would the technique shown in that answer work even though set wouldn't be called when setting a property of myAppDateFormatter? (A property's setter is not called if it is a class instance, and one of it's properties is being changed.)

Community
  • 1
  • 1
ConfusedByCode
  • 1,137
  • 8
  • 27
  • FWIW, setting the `dateFormat` on a date formatter is just as expensive as creating a new one so if you're trying to avoid the expensiveness of creating a date formatter by sharing one for the whole app you aren't actually accomplishing anything by doing it this way. – dan Mar 21 '17 at 15:39
  • According to [this](https://developer.apple.com/reference/foundation/dateformatter#overview), `DateFormatter` is thread safe on iOS 7 and later. – BallpointBen Mar 21 '17 at 15:43
  • @dan, interesting. I actually suspected that and meant to ask about it with my question. For some reason the developer who wrote the Obj-C code wrote it that way, but I was wondering if it was actually premature optimization. – ConfusedByCode Mar 21 '17 at 15:45
  • 1
    @BallpointBen It is thread safe in that multiple threads can use it to format things at the same time, but setting the `dateFormat` while another thread is using it to format something isn't safe. – dan Mar 21 '17 at 15:47
  • @dan, how do you know setting a `dateFormat` on a date formatter is just as expensive as creating a new one? The Apple documentation (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW10) says "creating a date formatter is not a cheap operation." By "creating" are they including setting the properties? – ConfusedByCode Mar 21 '17 at 16:04
  • I noticed it when profiling some code in instruments. I also remember hearing it in a WWDC video about performance from a few years back but I don't remember which one. – dan Mar 21 '17 at 16:10

1 Answers1

1

@synchronized is said to be equivalent to an NSRecursiveLock. I believe creating such a lock at the right scope and using it in the same places as you use @syncronized, wrapping it instead with a call to lock and unlock, is the most direct equivalent in Swift.

John Bushnell
  • 1,851
  • 22
  • 29