Resolved Declaring object inside a function

Discussion in 'iOS Programming' started by xArtx, Jun 2, 2013.

  1. xArtx, Jun 2, 2013
    Last edited: Jun 4, 2013

    xArtx macrumors 6502a

    Joined:
    Mar 30, 2012
    #1
    Hi Guys,
    Here's the best way I could think of to get the local time, and UTC time
    into C char arrays timechar, and utimechar:
    Code:
        // get local time
        NSDate *today = [NSDate date];
        NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
        [dateFormat setDateFormat:@"MM/dd/yyyy HH:mm:ss"];
        datestring = [dateFormat stringFromDate:today];    
        const char *c = [datestring UTF8String];    
        strncpy(timechar, c, 20);
        char done;
        char dtwo;
        done = timechar[0]; // swap date to Australian format
        dtwo = timechar[1];
        timechar[0] = timechar[3];
        timechar[1] = timechar[4];
        timechar[3] = done;
        timechar[4] = dtwo;
        
        // get utc time
        NSDateFormatter* dfutc = [[NSDateFormatter alloc] init];
        [dfutc setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
        [dfutc setDateFormat:@"MM/dd/yyyy HH:mm:ss"];
        datestring = [dfutc stringFromDate:today];
        const char *d = [datestring UTF8String];
        strncpy(utimechar, d, 20);
        done = utimechar[0]; // swap date to Australian format
        dtwo = utimechar[1];
        utimechar[0] = utimechar[3];
        utimechar[1] = utimechar[4];
        utimechar[3] = done;
        utimechar[4] = dtwo;
    
    Swapping the month and day manually was pretty silly when I could
    probably just change the formatter, but there are some really wild
    solutions on the net that are supposed to achieve the same thing.

    My question is if there is any overhead to do this in a function that is called often.
    By "this", I mean allocating the formatters inside the function,
    also with regard to other objects, but this one specifically uses the word "allocate".

    or is it better to:
    Code:
    // declare object
    NSDateFormatter* dfutc;
    
    // do this somewhere once in the program
    dfutc = [[NSDateFormatter alloc] init];
    
    // then continue to use the object inside the function
    
    Cheers, Art.
     
  2. protonail macrumors newbie

    protonail

    Joined:
    Jun 2, 2013
    #2
    I don't recommend to do premature optimizations. Did you check code with profiler and found performance problem in this piece of code? I think the answer is no, so use clear and predictable solutions in this case.
     
  3. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #3
    Do it once. I wouldn't consider it premature optimization because the code should be roughly the same length and equally readable and shouldn't be any more difficult to code. It becomes premature optimization when you have to write a lot of obscure code to get your performance boost.
     
  4. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #4
    It's just a question for Obj-C programming in general.
    With C, it would generally be bad to access global variables in functions,
    especially if the variable isn't needed outside of the function.

    With NSobjects, I don't know if the same applies.

    Performance analyser shows what I'd expect for a vector GPS,
    big crunch every second when it has to make calcs and draw a screen.
     
  5. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #5
    This is one typical way to do this using lazy allocation. You could store the date formatter as an ivar if that makes more sense or as a file scope global if you prefer that.

    Code:
    -(NSDateFormatter*)dateFormatter
    {
         static NSDateFormatter* formatter = nil;
         if (! formatter)
         {
              formatter = [[NSDateFormatter alloc] init];
             [formatter setDateFormat:@"MM/dd/yyyy HH:mm:ss"];
         }
         return formatter;
    }
    The date formatter has to access quite a lot of info related to locales to do its thing so it is considered somewhat heavyweight to create one.
     
  6. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #6

    I agree. Memory allocation is a fairly expensive operation.

    Lazy loading is a good way to do this. You could create a method

    Code:
    -(NSDateFormatter *)theDateFormatter;
    {
      if (_theDateFormatter = nil)
      {
        _theDateFormatter = [[NSDateFormatter alloc] init];
        //configure the date formatter as needed.
      }
      return _theDateFormatter;
    }
    Then, any time in your code that you need to refer to your date formatter, use "[self theDateFormatter]".

    If you're writing C functions, not methods, then you might need to create a Utilities class and make your theDateFormatter method be a Utilities class method that saves it's date formatter to a private static variable.

    The overhead of a method call is dwarfed by the overhead of memory allocation. In fact, the runtime caches method selector lookup and short-circuits method calls into C function calls, so repeated method calls are nearly as fast as function calls.
     
  7. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #7
    If there is any possibility of the function or method being called from multiple threads, then the lazy instantiation of the singleton object should be made thread-safe. If it's not, Bad Things may result.

    Example of thread-safe (and unsafe) code:
    http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html

    Google search terms: cocoa singleton


    To the OP:
    Fix the format string so it doesn't require the silly and obtuse shuffling of characters after conversion. There is no reason to keep such horrible code while also worrying about speed. Yes, you'll have to learn what the formatting characters in the format string do, rather than blindly adding copy-pasta with after-the-fact "fixes".
     
  8. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #8
    Thanks for the replies.
    I thin I can work with the example given,
    and I've seen some sample code that does the same thing...
    I think it's actually OpenEars (flite) lib that does it.

    I kinda did say that.
    This was the first thing I got working.
    Prior to that I was reading the timezone difference, and applying a difference
    to the value for hours input to some algorithms. Now I can input UTC.

    Looking a bit harder at my copy-pasta fix, I should have used one less variable:
    Code:
       	char done;
    	done = timechar[0]; // swap date to Australian format
    	timechar[0] = timechar[3];
    	timechar[3] = done;
    	done = timechar[1];
    	timechar[1] = timechar[4];
    	timechar[4] = done;
    
    That's a much more worthy hack job!
     
  9. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #9
    ... If that makes sense, comment it. I have no idea what you're doing or why you're doing it, and I doubt you will either after setting the file down for a few months then looking at it again if you don't comment it.
     
  10. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #10
    It is commented: //swap date to Australian format.
    In Australia, we write today's date: 03/06/13.
    All I'm doing is swapping the two chars in the result string,
    where I should have just changed the date formatter.

    It's no biggie. I'll eventually have to do it based on timezone.
    It's small time compared to something like converting Kilometers to Miles for other countries.
     
  11. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #11
    Formatting dates for the local conventions of the user is exactly what date formatters are for.

    Whenever possible, you should ask for dates to be formatted in the most general way possible (e.g. short date format) and then let the date formatter generate the string for you using the user's current locale. Date formatters know about the date conventions in a huge number of different languages and countries, and will take care of it for you, without any special code on your part. Here in the US, we'll get our silly mm/dd/yy format. In Oz, and in much of Europe, you'll get dd/mm/yy. If you want 3 letter month codes, those will be converted to the correct language format.

    Using a date formatter and then byte-swapping the strings that come out of it is a bad idea, because in other countries the output date format may be different, causing your code to corrupt the date.
     
  12. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #12
    People outside Australia may not know what "Australian format" means. I sure didn't, and had to read and comprehend the byte shuffling in order to know what it meant.

    If you had commented "swap to dd/MM/yyyy format", it would have been clearer. However, it would be even more puzzling as to why you didn't just use that as the format string in the first place.

    Obtuse code should be fixed, not simply commented, unless there's a good reason for the obtuseness to remain. I see no such reason here.
     
  13. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #13
    I didn't know it would know to change the format, thx.

    So I was able to declare the objects globally,
    and reduce what's in the C function to this:
    Code:
      today = [NSDate date];
    
      // get local time
      datestring = [dateFormat stringFromDate:today];    
      const char *c = [datestring UTF8String];    
      strncpy(timechar, c, 20);
        
      // get utc time
      datestring = [dfutc stringFromDate:today];
      const char *d = [datestring UTF8String];
      strncpy(utimechar, d, 20);
    
    I did the allocation in an "if" that is only runs once, and contains
    code to to other initialisation as well:
    Code:
    if (trig == 0) {
        dateFormat = [[NSDateFormatter alloc] init];
        [dateFormat setDateFormat:@"dd/MM/yyyy HH:mm:ss"];   
        dfutc = [[NSDateFormatter alloc] init];
        [dfutc setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
        [dfutc setDateFormat:@"dd/MM/yyyy HH:mm:ss"]; 
    
    // do other initialisation stuff for example,
    pidivtwo = PI / 2.0; pitimestwo = PI * 2.0;
    
    // lock this code for future passes
    trig = 1;
    } 
    
    Thing is, the format needs to be what I expect, in order to input it
    into a bunch of calculations. I wouldn't be able to format it to local
    conventions until it comes time to display it.

    Anyway, it works...
     
  14. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #14
    Why not pull the components out of the NSDate as needed for the calculations? Have you heard of NSDateComponents?
     
  15. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #15
    Sorry, I made a mistake there...

    I'm getting UTC and Local time into two separate strings.

    UTC time is used for the calculations (along with TZ offset if needed),
    and local time is used for display.
    So I could mess up the local time at will without consequence.

    I guess this is resolved, though I still want to load the month and day chars into
    a four byte array and bitwise rotate the array 16 times to swap the dates.
     
  16. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #16
    Obviously not!
    Code:
        int myear = 2000, uyear = 2000; // will expire 2099
        int mmonth = 0, umonth = 0;
        int mday = 0, uday = 0;
        int mhour = 0, uhour = 0;
        int mmin = 0;
        int umin = 0;
        int msec = 0;
       // local time components
        myear = myear + ((timechar[8] - 0x30)*10);
        myear = myear + (timechar[9] - 0x30);
        mmonth = mmonth + ((timechar[3] - 0x30)*10);
        mmonth = mmonth + (timechar[4] - 0x30);        
        mday = mday + ((timechar[0] - 0x30)*10);
        mday = mday + (timechar[1] - 0x30);        
        mhour = mhour + ((timechar[11] - 0x30)*10);
        mhour = mhour + (timechar[12] - 0x30);
        mmin = mmin + ((timechar[14] - 0x30)*10);
        mmin = mmin + (timechar[15] - 0x30);
        msec = msec + ((timechar[17] - 0x30)*10);
        msec = msec + (timechar[18] - 0x30);
        // UTC time components
        uyear = uyear + ((utimechar[8] - 0x30)*10);
        uyear = uyear + (utimechar[9] - 0x30);
        umonth = umonth + ((utimechar[3] - 0x30)*10);
        umonth = umonth + (utimechar[4] - 0x30);
        uday = uday + ((utimechar[0] - 0x30)*10);
        uday = uday + (utimechar[1] - 0x30);
        uhour = uhour + ((utimechar[11] - 0x30)*10);
        uhour = uhour + (utimechar[12] - 0x30);
        umin = umin + ((utimechar[14] - 0x30)*10);
        umin = umin + (utimechar[15] - 0x30);
    
    but I have no use for this code anymore.
     
  17. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #17
    Oh man. NSCalendar, NSDate, and NSDateComponents have all kinds of handy methods for doing date/time calculations. Do a search in the Xcode docs under "Performing Calendar Calculations" and read the article that comes up. Also read the whole introduction to the NSCalendar class.

    Methods like components:fromDate:, components:fromDate:toDate:, and dateByAddingComponents:toDate:eek:ptions: are very powerful and very useful.

    As you say, you can chuck all that nasty code above.
     
  18. xArtx, Jun 4, 2013
    Last edited: Jun 4, 2013

    xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #18
    Whoah, Hmm. Unless it can look at say, something like,
    what hour is it in UTC timezone without calculating every other component,
    wouldn't calculating only the components you need always be faster?

    So in English, if I only wanted to know the Time Of Day in some country,
    and I have their timezone offset, couldn't you always calculate the result
    time faster because you didn't reference a calendar to look at other information?

    For a particular thing I'm doing, I want to display the Time 00:00 in any
    timezone, but the time only. It doesn't even have to know if it is the day
    before, or after any other reference timezone.

    Admittedly, I haven't RTM, but I will.
     
  19. ArtOfWarfare, Jun 4, 2013
    Last edited: Jun 5, 2013

    ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #19
    You sound like you're contemplating premature optimization. Don't optimize code until you know it's slow.
     
  20. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #20
    The learning curve for me is a factor. I already know how to do what I'm already doing.
    It's not as appealing to learn if I don't think the results will be better than
    what I'm already doing (basically) in a particular project.

    Don't get me wrong, I've already seen some nice examples,
    and in this case it's something I'm going to have to learn anyway.
    It's not like a future platform is going to arrive that doesn't have it's
    own clock and calendar, so it doesn't seem any less portable to use one.
     
  21. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #21
    Processing speed is not usually a consideration when writing this type of code. Unless you are processing tens of thousands of dates you would need sensitive measurements to detect the difference in performance.

    Maintainability and ease of localization is much, much more important. I find NSCalendar, NSDate, and NSDateComponents very easy to work with, and the logic is flawless. Your code that calculates month numbers quite likely has edge case problems. Date calculations are a royal pain to get perfect (and test to verify that they are perfect.)
     
  22. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #22
    I don't disagree (generally), and it probably does sound anal or lazy of me,
    but I do want to process an unusually high number of timezones. I want to recreate this:
    http://www.youtube.com/watch?v=g0kr7hbN744
    on the iOS platform.

    It's only the fancy display that requires all the checking, otherwise you could
    just check the time at the start and finish point on the map.
    As a bi more of an "adult", and with the conveniences this platform offers,
    I would be checking inside timezone polygons this time.

    It is just an excersise for me, but one concrete practical example is to stop
    people cheating in games by changing their time zone in settings.
    This can determine your timezone without the need for looking at the clock,
    as long as the user agrees to Location Services (which could be a requirement).
     
  23. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #23
    So use NSTimeZones with customized offsets, if you want to support additional time zones.
     

Share This Page