3 Replies
      Latest reply: Oct 7, 2016 2:40 AM by eskimo RSS
      Ghotel Level 1 Level 1 (0 points)

        So far I've been using this code snippet:

         

        + (id)stringWithFormat:(NSString *)format array:(NSArray*)arguments { 
            NSRange range = NSMakeRange(0, [arguments count]); 
            NSMutableData* data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]]; 
            [arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range]; 
            NSString* result = [[NSString alloc] initWithFormat:format arguments:data.mutableBytes]; 
        return result;
        }
        
        
        

         

        Since update to Xcode 8 I'am getting EXC_BAD_ACCESS on line 5. Any ideas how to create va_list from array using XCode-8?

        • Re: NSArray to va_list arguments and string format XCode 8
          eskimo Apple Staff Apple Staff (6,470 points)

          What are you expecting this code to do?  Can you give me an example of its use.

          In general, the set of things that you can legally do with a va_list is tiny, so I’m not super surprised that this code was broken by a compiler update.  However, before I can make suggestions as to how to proceed I’d like to get a better understanding of your final goạl.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: NSArray to va_list arguments and string format XCode 8
              Ghotel Level 1 Level 1 (0 points)

              My expectations are pretty simple. We've got api-endpoints format
              e.g settings/user/%@/newSettings/%i
              as well as serialized JSONResponse with parameters (string). Paramters are stored in NSArray because of JSONModel mapping - this can be strings, integers etc.. My code requires

              initWithFormat: arguments:
              
              

               

              being called with data provided from NSArray. That's why I was trying to "pack" data into va_list.
              Interesting thing is that I just tested it on xcode7 (with ARM64 architecture checked) and everything seems to be fine.

                • Re: NSArray to va_list arguments and string format XCode 8
                  eskimo Apple Staff Apple Staff (6,470 points)

                  We've got api-endpoints format e.g settings/user/%@/newSettings/%ias well as serialized JSONResponse with parameters (string). [Parameters] are stored in NSArray because of JSONModel mapping - this can be strings, integers etc.

                  There isn’t a supported way to do this.  va_list is defined by the C standard and that standard defines a very limited set of operations on it (you can see the full list here).  None of those operations let you build a va_list except by applying va_start within a varargs function.  Building a va_list by hand may work on some specific architectures but you will inevitably run into problems (either on new architectures, as the OS and compiler evolves).

                  I also want to clarify the example you showed above.  Presumably, the end result is a call like this:

                  str = [SomeClass stringWithFormat:@"settings/user/%@/newSettings/%i"
                      array:@[ @"eskimo1", @42 ]
                  ];
                  

                  How did your old code work in the %i case?  When you call -getObjects:range:, the value you get back is an (NSNumber *) not the int that’s specified by the %i format specifier.

                  Consider this code:

                  NSArray * arguments = @[ @"eskimo1", @42 ];
                  
                  NSRange range = NSMakeRange(0, [arguments count]); 
                  NSMutableData* data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]]; 
                  [arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range];
                  NSLog(@"%@", data);
                  

                  which prints on my Mac:

                  <48520000 01000000 272a0000 00000000>
                  

                  Remember that this is 64-bit big endian, so the actual values are:

                  • 0x0000000100005248

                  • 0x000000000000272a

                  The first one is an object pointer for @"eskimo1", which makes sense and is compatible with the %@.  The second is also an object pointer, but in this case it’s a tagged pointer for @42, and that won’t print properly if you feed it to %i (even if you ignore the fact that %i expects an int, which is 32-bits on our platforms).

                  If I were in your shoes I’d constrain the allowed format specifiers in the the format string and then manually parse the string, find the format specifiers, and then render that item of the array in a way that’s appropriate for that specifier.  This would yield a bunch of advantages:

                  • it will actually work (-:

                  • it will allow to to check that the array contains the right number of arguments

                  • it will allow you to type check each argument (at runtime, but that’s better than nothing)

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"