In Swift 2.0 what is the maximum length of a string? - ios

I have an application which utilizes a single string. This string contains data loaded from an array and then the string is exported to a text file.
My question is what is the longest length possible for this string, and when does it become a problem that it is getting too long?

Following the official Apple documentation:
String is bridged to Objective-C as NSString, and a String that
originated in Objective-C may store its characters in an NSString.
Since all devices were capable of running iOS are 32 bit, this means NSUIntegerMax is 2^32.
According to Swift opensource GitHub repo It would seem that its value is 2^64 = 18,446,744,073,709,551,615 ; hexadecimal 0xFFFFFFFFFFFFFFFF for the 64 bit devices, following this code:
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
// + (instancetype)
// stringWithCharacters:(const unichar *)chars length:(NSUInteger)length
...
maxLength:(NSUInteger)maxBufferCount
...
TEST: (on iPhone 6)

String is bridged to Objective-C as NSString, and a String
that originated in Objective-C may store its characters in an
NSString. Since any arbitrary subclass of NSString can
become a String, there are no guarantees about representation or
efficiency in this case.
What is the maximum length of an NSString object?
The hard limit for NSString would be NSUIntegerMax characters. NSUIntegerMax is 2^32 - 1 and NSString can hold a little over 4.2 billion characters.
According comments:
for iPhone 5S and above since they are 64 Bit. It's 2^(64 - 1)

Related

How do I convert NSString to an encoding other than UTF-8?

I'm working with c in iOS Project I'm trying to convert my string to respected type in c , below code is supposed to send to core Library
typedef uint16_t UniCharT;
static const UniCharT s_learnWord[] = {'H', 'e','l','\0'};
what i have done till now is string is the one what I'm passing
NSString * string = #"Hel";
static const UniCharT *a = (UniCharT *)[string UTF8String];
But it is failing to convert when more than one character , If i pass one character then working fine please let me where i miss, How can i pass like s_learnWord ?
and i tried in google and StackOverFLow none of the duplicates or answers didn't worked for me like this
Convert NSString into char array I'm already doing same way only.
Your question is a little ambiguous as the title says "c type char[]" but your code uses typedef uint16_t UniCharT; which is contradictory.
For any string conversions other than UTF-8, you normally want to use the method getCString:maxLength:encoding:.
As you are using uint16_t, you probably are trying to use UTF-16? You'll want to pass NSUTF16StringEncoding as the encoding constant in that case. (Or possibly NSUTF16BigEndianStringEncoding/NSUTF16LittleEndianStringEncoding)
Something like this should work:
include <stdlib.h>
// ...
NSString * string = #"part";
NSUInteger stringBytes = [string maximumLengthOfBytesUsingEncoding];
stringBytes += sizeof(UniCharT); // make space for \0 termination
UniCharT* convertedString = calloc(1, stringBytes);
[string getCString:(char*)convertedString
maxLength:stringBytes
encoding:NSUTF16StringEncoding];
// now use convertedString, pass it to library etc.
free(convertedString);

How to convert NSData to struct accurately [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I Got a data from device(BLE):<840100ec d5045715 00010014 00240018 00>
but the second byte can not convert accurately. Like these:
But I can do it use Uint8 array, Why? Thank you.
code like these:
// I got the data:<840100ec d5045715 00010014 00240018 00>
case SPK_FEEDBACK_HistoryDataPort:
// Log
NSLog(#"receive data:%#", [NSData dataWithBytes:originalCommandBytes length:sizeof(D2MHistoryDataPort)]);
// originalCommandBytes dataType:UInt8 *
D2MHistoryDataPort *historyData = (D2MHistoryDataPort *)originalCommandBytes;
// Log
NSLog(#"收到硬件返回的0x%x指令(历史数据体): 历史数据包的索引:%d; 时间戳:%d; 步数:%d; 卡路里:%d; 距离:%d; 睡眠:%d; 运动时长:%d",
historyData->cmd,
historyData->index,
(unsigned int)historyData->timeStamp,
historyData->steps,
historyData->calories,
historyData->distance,
historyData->sleep,
historyData->duration);
break;
// I declare this struct in another class
typedef struct {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;
How the compiler lays out the individual fields of a struct in memory is implementation dependent. Usually the compiler has to add padding to properly align the fields and it might even reorder them (by grouping fields of the same size) to reduce the required padding and the overall size of the struct.
You can turn this behavior off using __attribute__((packed)):
typedef struct __attribute__((packed)) {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;
What you are doing is quite guaranteed to not work. You are trying to take a struct, assume that you can interpret it as a sequence of bytes, write it and read it back. That's not going to work. Starting with structs having different layouts between compiler versions, between 32 and 64 bit compilers, and so on. People knew in the '90s that this was a bad idea.
Use the NSCoding protocol. Or convert the data to JSON. Do NOT try to interpret structs as a sequence of bytes.
If you absolutely cannot avoid using NSData, this is how it works safely:
Step 1: Define the external data format. The external data format is NOT "whatever the compiler decided to layout my struct". The external data format is "One unsigned byte cmd; two unsigned bytes index, most significant byte first. 4 unsigned bytes time stamp, most significant byte first, meaning the number of seconds since Jan 1st 1904, ... " and so on.
Then to read the struct, get a pointer to the first byte, check that you have enough bytes, and write
mystruct.cmd = p [0];
mystruct.index = (p [1] << 8) | p [2];
mystruct.timeStamp = (p [3] << 24) | (p [4] << 16) ...
and so on.

Formatting NSUInteger with %i

I am going through a former employees code and about 20 of these warnings show up:
Values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead
one part of the code where this arises is:
NSUInteger Length;
With:
- (NSString *) description {
// If no value was given, display type
if([Value length] == 0)
{
NSString *type = #"";
switch (Type) {
case DTCompType_Year: type = #"year";
break;
case DTCompType_Month: type = #"month";
break;
case DTCompType_Day: type = #"day";
break;
case DTCompType_Hour: type = #"hour";
break;
case DTCompType_Minute: type = #"minute";
break;
case DTCompType_Second: type = #"second";
break;
case DTCompType_Meridiem: type = #"meridiem";
break;
case DTCompType_MonthName: type = #"month_name";
break;
case DTCompType_DayOfTheWeek: type = #"day_of_the_week";
break;
case DTCompType_Undefined: type = #"undefined";
break;
}
return Length == 0 ? [NSString stringWithFormat:#"[%#]", type] :
[NSString stringWithFormat:#"[%#:%i]", type, Length];
}
No where in apples documentation can I find %i
Apple's Documentation
I have never worked with Objective-C before, and now I have to update this app. I understand that this needs to become an unsigned long, but I don't want to start changing things without knowing why. The app works just fine as is, so are there any inherent consequences for changing these to unsigned long? or even changing the format specifier from %i to %lu?
From what I've read, it could be a matter of the platform. (32-bit vs 64-bit)
This was developed for an iPad 2 in iOS7, and we just upgraded the SDK to iOS8.
I found this post:
NSUInteger should not be used in format strings?
which has given me some guidance, but I need more clarification.
%i is equivalent to %d. Technically, you should have been using %u anyway. The problem is, as you suspect, 32-bit vs 64-bit; NS[U]Integer is [unsigned] int on 32-bit builds, but [unsigned] long on 64-bit ones. Because the iPhone is little-endian, it will "work" as long as the %i/d/u is the last format specified, but it's still wrong. You should cast the argument to be the type the format specifier expects (int/long/unsigned/unsigned long), as the warning message tells you to.
From <objc/NSObjCRuntime.h>:
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
You can use a boxed literal to allow the compiler and the NSNumber class to handle the details of converting between the various numeric types and their string representations. For example, given the following variable definition...
NSUInteger foo = 42;
...you can create an instance of NSNumber as follows:
NSNumber *myNumber = #(foo);
You can then use the %# format specifier whenever you need to format the value of myNumber. Of course it's easy enough to instead box the original numeric value right in line:
NSString *s = [NSString stringWithFormat:#"The answer is %#", #(foo)];

Why isn't NSInteger defined as long on 32-bit iOS?

NSInteger is defined this way:
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
#else
typedef int NSInteger;
#endif
This results in NSInteger being defined as int on 32-bit iOS even though int and long are the same anyway (both 4 bytes). Format strings like the following generate a warning with this definition:
NSInteger x = 4;
[NSString stringWithFormat: #"%ld", x];
// Warning: Values of type 'NSInteger' should not be used as format arguments;
// add an explicit cast to 'long' instead.
So does somebody know why NSInteger isn't always defined as long?
Historical reasons, where previous releases of APIs used int -- then were migrated to use typedef NSInteger ca. the 64-bit transition of OS X.
I suppose they could have changed for iOS, but that would have impacted a lot of existing and to-be-developed code if they were different on OS X and iOS.

NSData to primitive

I have NSData *data. It's value is 000e.
Hence decimal value is 14.
Now I want to get this value into primitive NSUInteger.
I've tried
NSUInteger *hereIWant14 = (NSUInteger *)data.bytes;
but *hereIWant14 value is 3584 what is e00 in hexa. Now I don't know if there is problem with endians or type size or my thinking is completely wrong and 000e and e00 similarity is just pure accident.
It's related to endianness. Use the conversion macros defined in Endian.h:
EndianU16_BtoN(value)
EndianU16_NtoB(value)
EndianS32_BtoN(value)
EndianS32_NtoB(value)
EndianU32_BtoN(value)
EndianU32_NtoB(value)
EndianS64_BtoN(value)
EndianS64_NtoB(value)
EndianU64_BtoN(value)
EndianU64_NtoB(value)
etc.
Method signatures mean:
Endian + U for unsigned S for signed + number of bits + N is native endianness of the system, L is little endian, B is big endian
So if you have an NSData with the content (0x00, 0xE0), and you'd like to interpret it as the value 14, then it contains the data in big endian order, therefore you'll have to use EndianU32_BtoN (of course, this macro is the identity transform on big-endian systems and swaps the bytes only on little-endian machines).
Important: to be future-proof, I'd recommend you to use something else instead of NSUInteger, since the width of this type can vary between different systems (e.g. Apple defines NSUInteger as 64 bit on arm64 systems). So, to be explicit about the number of bits, use uint32_t or uint64_t etc.
Edit: Usage for Big-Endian short value
NSMutableData * data = [[NSMutableData alloc] initWithLength:2];
((unsigned char *)data.mutableBytes)[0] = 0x00;
((unsigned char *)data.mutableBytes)[1] = 0x0E;
NSUInteger integer = NSSwapBigShortToHost(*(unsigned short *)data.mutableBytes);
NSLog(#"%d", integer); // prints 14
Yes, it's because of the endianess as said.
If you need down here two pieces of code to read from a NSData:
u_int16_t signature;
[data getBytes:&signature range:NSMakeRange(0, 2)];
signature = CFSwapInt16(signature);
Usually I use CFSwapInt16 or CFSwapInt32 (for u_int32_t). Otherwise if you have to read for example a string:
char charArray[length];
[data getBytes:charArray range:NSMakeRange(0, length)];
NSData* data = [NSData dataWithBytes:charArray length:length];
NSString* string = [[NSString alloc]initWithData:data encoding:NSStringEncodingConversionAllowLossy];
Hope it can help!

Resources