Reference Counts with fixed string literals vs initWithString: in NSString class? - ios

I was just dabbling in reference counting and was using NSString objects to understand.
1st piece of code:
NSString* a1 = [[NSString alloc] initWithString:#"abc"];
NSLog (#"%d %# %p",[a1 retainCount],a1,a1);
NSString* a2 = [a1 copy];
NSLog (#"%d %d %# %# %p %p",[a1 retainCount],[a2 retainCount],a1,a2,a1,a2);
NSString* a3 = a1;
NSLog (#"%d %d %d %# %# %# %p %p %p", [a1 retainCount],[a2 retainCount],[a3 retainCount],a1,a2,a3,a1,a2,a3);
NSString* a4 = [a1 retain];
NSLog (#"%d %d %d %d %# %# %# %# %p %p %p %p",[a1 retainCount],[a2 retainCount],[a3 retainCount],[a4 retainCount],a1,a2,a3,a4,a1,a2,a3,a4);
NSString* a5 = [a1 retain];
NSLog (#"%d %d %d %d %d %# %# %# %# %# %p %p %p %p %p", [a1 retainCount],[a2 retainCount],[a3 retainCount],[a4 retainCount],[a5 retainCount],a1,a2,a3,a4,a5,a1,a2,a3,a4,a5);
This gives output:
2014-06-08 00:25:33.382 demo[13091] 1 abc 0x699100
2014-06-08 00:25:33.383 demo[13091] 2 2 abc abc 0x699100 0x699100
2014-06-08 00:25:33.383 demo[13091] 2 2 2 abc abc abc 0x699100 0x699100 0x699100
2014-06-08 00:25:33.383 demo[13091] 3 3 3 3 abc abc abc abc 0x699100 0x699100 0x699100 0x699100
2014-06-08 00:25:33.383 demo[13091] 4 4 4 4 4 abc abc abc abc abc 0x699100 0x699100 0x699100 0x699100 0x699100
This is understandable since whenever a NSString object is created (empty or with initWithString:) and copied, retained etc then reference count for that object will increase by 1 and all references will keep pointing to same object.
2nd piece of code:
NSString* a1 = [[NSString alloc] init];
a1 = #"abc";
NSLog (#"%d %# %p",[a1 retainCount],a1,a1);
NSString* a2 = [a1 copy];
NSLog (#"%d %d %# %# %p %p",[a1 retainCount],[a2 retainCount],a1,a2,a1,a2);
a1 = #"xyz";
NSString* a3 = a1;
NSLog (#"%d %d %d %# %# %# %p %p %p", [a1 retainCount],[a2 retainCount],[a3 retainCount],a1,a2,a3,a1,a2,a3);
a1 = #"pqr";
NSString* a4 = [a1 retain];
NSLog (#"%d %d %d %d %# %# %# %# %p %p %p %p",[a1 retainCount],[a2 retainCount],[a3 retainCount],[a4 retainCount],a1,a2,a3,a4,a1,a2,a3,a4);
a1 = #"abc";
NSString* a5 = [a1 retain];
NSLog (#"%d %d %d %d %d %# %# %# %# %# %p %p %p %p %p", [a1 retainCount],[a2 retainCount],[a3 retainCount],[a4 retainCount],[a5 retainCount],a1,a2,a3,a4,a5,a1,a2,a3,a4,a5);
This gives output:
2014-06-08 00:27:34.940 demo[23649] 1 abc 0x601610
2014-06-08 00:27:34.941 demo[23649] 1 1 abc abc 0x601610 0x601610
2014-06-08 00:27:34.941 demo[23649] 1 1 1 xyz abc xyz 0x6015b0 0x601610 0x6015b0
2014-06-08 00:27:34.941 demo[23649] 1 1 1 1 pqr abc xyz pqr 0x601570 0x601610 0x6015b0 0x601570
2014-06-08 00:27:34.941 demo[23649] 1 1 1 1 1 abc abc xyz pqr abc 0x601610 0x601610 0x6015b0 0x601570 0x601610
Queries:
Since NSString object are immutable how is the compiler allowing me to change value of variable a1?
I have no idea how the reference count for all variables is coming out be 1 even when a5 is retaining same object which is pointed to by a1 (based on memory location)?
How are string literals stored in Objective C? I've been told they're stored on the heap but if someone could give a detailed explanation or point me in the right direction, that'd be very helpful.
Thanks in advance
Nick

For both 1 and 2: On your second block of code you're not actually changing the value of a1. Mutability has to do with inserting or removing characters, for example.
Here you're making a1 point to a new object, which in turn should release the previous one (depending on its retain count) and retain the newly assigned.
So when you create a2, a3, a4 and a5 right after having a1 point to a new object each time, this new object has a retain count of 1.
For the third query, this other question might be of interest: Are NSStrings stored on the heap or on the stack and what is a good way to initialize one

Related

Separate string with format into array

I am trying to separate the values of a formatted string into an array. I have the string specifying format but cannot find a way to get the values of a formatted string given the format.
Here is the format string:
fmt=['%2i %2i %4i %2i %2i %2i %1i %11.7f %11.7f %12.5f %12.5f ' ...
'%5.2f %11.5f %5.2f %6.2f %2i %1i %9.5f %9.5f %3i\n'];
And here is an example of a formatted string that I'd like to separate:
5 27 2015 2 21 17 0 32.3788833 -64.6799500 6.16800 -0.12000 5.53 0.36000 5.40 6.03 4 4 -99.99999 -99.99999 999
Is there any way of doing this? If not, in objective-c can I replace consecutive spaces with one space and then separate with one space?
Hope this helps,
NSString *yourStr = #"5 27 2015 2 21 17 0 32.3788833 -64.6799500 6.16800 -0.12000 5.53 0.36000 5.40 6.03 4 4 -99.99999 -99.99999 999 ";
NSPredicate *nonEmptyValue = [NSPredicate predicateWithFormat:#"SELF != ''"];
NSArray *parts = [yourStr componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *finalArr = [parts filteredArrayUsingPredicate:nonEmptyValue];
You can use NSString's componentsSeparatedByString: to convert your string into an NSArray.
Example:
NSString *string = #"5 27 2015 2 21 17 0 32.3788833 -64.6799500 6.16800 -0.12000 5.53 0.36000 5.40 6.03 4 4 -99.99999 -99.99999 999";
NSArray *array = [string componentsSeparatedByString:#" "];
P.S. Make sure you clean your string from leading/trailing spaces and convert multiple consecutive spaces into one single space character.

IOS:Convert hex values from the characterstic.value result

i am able to retrieve value from the peripheral as hex value and i need to convert as per my requirement.[24/12/14 11:37:00 am] sonali_phatak: I can see that i have received proper response.from 01117100352e36302e313100000000e55a
01 - 01-start byte
11 - 17(Dec) - length of responce packet
71 - response ID
00 - Ignore this byte
So now out of total length 17, first 4 bytes are header, last 2 bytes are CRC. We
need to read remaining 11 bytes and convert them to ASCII.
35 - 5
2e - .
36 - 6
30 - 0
2e - .
31 - 1
31 - 1
So Iam getting version number from watch as 5.60.11
But i need to show the above value 5.60.11 in string and print in console . how to convert it pleas help me
Please try this :
NSString *strOriginalHex= #"01117100352e36302e313100000000e55a";
NSString *strNewHexForVersion = [strOriginalHex substringWithRange:NSMakeRange(8, 14)];
NSLog(#"%#",[self stringFromHexString:strNewHexForVersion]);//5.60.11
- (NSString *)stringFromHexString:(NSString *)aStrHexString
{
// The hex codes should all be two characters.
if (([aStrHexString length] % 2) != 0)
return nil;
NSMutableString *aMutStrNewString = [NSMutableString string];
for (NSInteger i = 0; i < [aStrHexString length]; i += 2)
{
NSString *hex = [aStrHexString substringWithRange:NSMakeRange(i, 2)];
NSInteger decimalValue = 0;
sscanf([hex UTF8String], "%x", &decimalValue);
[aMutStrNewString appendFormat:#"%c", decimalValue];
}
return aMutStrNewString;
}

How to print Tagged Pointer for NSNumber?

I read it's article and i try print Tagged Pointer for NSNumber
I try in xcode follow code:
- (NSString *)binForObjectPointer:(NSObject *)obj
{
return [self binForScalarNumber:(uintptr_t)(__bridge void *)(obj)];
}
- (NSString *)binForScalarNumber:(uintptr_t)number
{
NSString *ms = #"";
while (number)
{
if (number & 1)
ms = [ms stringByAppendingString:#"1"];
else
ms = [ms stringByAppendingString:#"0"];
number >>= 1;
}
return ms;
}
for (int i = 0; i < 10; ++i)
{
NSNumber *n = [NSNumber numberWithInt:i];
NSString *s = [self binForObjectPointer:n];
NSLog(#"%i %# %# %#",
i,
[s substringWithRange:NSMakeRange(0, s.length - 4)],
[s substringWithRange:NSMakeRange(s.length - 4, 3)],
[s substringWithRange:NSMakeRange(s.length - 1, 1)]
);
}
and get result in console
2014-09-25 16:57:46.926 AppName[3983:60b] 0 0000001111100101001010100 110 1
2014-09-25 16:57:46.930 AppName[3983:60b] 1 0000010000000101001010100 110 1
2014-09-25 16:57:46.931 AppName[3983:60b] 2 0000000001110101001010100 110 1
2014-09-25 16:57:46.932 AppName[3983:60b] 3 0000011011111101110001100 110 1
2014-09-25 16:57:46.933 AppName[3983:60b] 4 0000011011000110000110100 110 1
2014-09-25 16:57:46.934 AppName[3983:60b] 5 0000101101000110101001100 110 1
2014-09-25 16:57:46.935 AppName[3983:60b] 6 0000100110100110111010100 110 1
2014-09-25 16:57:46.936 AppName[3983:60b] 7 0000000111011010101001100 110 1
2014-09-25 16:57:46.938 AppName[3983:60b] 8 0000001100000110101010100 110 1
2014-09-25 16:57:46.939 AppName[3983:60b] 9 0000001011000110000110100 110 1
It doesn't look like in article example:
0000 0000 0000 0000 0000 0000 0011 1011
^ ^ ^ tag bit
| |
| tagged pointer class (5)
|
binary 3
When my error?

F# OleDb Syntax Error in INSERT INTO Statement Pulling Data from Access to Linked SQL Server

I'm running an F# application to pull data from an Access table to a linked SQL Server table. Here is the resulting query:
INSERT INTO dbo_TempTerm (UnitID, PolicyTermYear, InsuredName, PolicyNumber, RenewalDate, CovATotal, CovBTotal, CovLTotal, DwellExtn, AllOtherPerilDeductible, MedPay, TotalPremium, HurricaneDeductible, Zone, Subzone, PercentCRC, PercentCRD, YearBuilt, RenYrs, PercentCFD, PercentHA, PercentMLD, GRP1, PercentNH, QCLM, RateV, CRI, AgentCode, AgentName, AFOCode, PolicyType, PolicyForm, OC, DateCreated)
SELECT [UNIT ID], [POLICY TERM YEAR], [INSURED NAME], [POLICY #], [RENEWAL DT], [COV A TOTAL], [COV B TOTAL], [COV L TOTAL], [DWELL EXTN], [ALL/OTHER PERIL DEDUCTIBLE], [MED PAY], [TOT PREMIUM], [HURR DED], ZONE, SUBZONE, [%CRC], [%CRD], [YR BLT], RENYRS, [%CFD], [%HA], [%MLD], GRP1, [%NH], QCLM, [RATE V], CRI, [AGENT CODE], [AGENT NAME], [AFO CODE], [POLICY TYPE], [POLICY FORM], OC, DateCreated
FROM tblPrior
The query parses and runs fine in Access. But from within my application, it raises an error: Syntax error in INSERT INTO statement.
Here is my F#, though I don't think it's relevant.
module Data
open System.Data
open System.Data.OleDb
type Period = Prior | Current
let Upload (p:Period) db =
use conn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + db + ";Persist Security Info=False;")
let execNonQuery s =
let comm = new OleDbCommand(s, conn) in
comm.ExecuteNonQuery() |> ignore
let (table,proc) = match p with
| Prior -> ("tblPrior" ,"InitPriorTable")
| Current -> ("tblCurrent","InitCurrentTable")
do conn.Open()
let u = sprintf "INSERT INTO dbo_TempTerm (UnitID, PolicyTermYear, InsuredName, PolicyNumber, RenewalDate, CovATotal, CovBTotal, CovLTotal, DwellExtn, AllOtherPerilDeductible, MedPay, TotalPremium, HurricaneDeductible, Zone, Subzone, PercentCRC, PercentCRD, YearBuilt, RenYrs, PercentCFD, PercentHA, PercentMLD, GRP1, PercentNH, QCLM, RateV, CRI, AgentCode, AgentName, AFOCode, PolicyType, PolicyForm, OC, DateCreated) SELECT [UNIT ID], [POLICY TERM YEAR], [INSURED NAME], [POLICY #], [RENEWAL DT], [COV A TOTAL], [COV B TOTAL], [COV L TOTAL], [DWELL EXTN], [ALL/OTHER PERIL DEDUCTIBLE], [MED PAY], [TOT PREMIUM], [HURR DED], ZONE, SUBZONE, [%%CRC], [%%CRD], [YR BLT], RENYRS, [%%CFD], [%%HA], [%%MLD], GRP1, [%%NH], QCLM, [RATE V], CRI, [AGENT CODE], [AGENT NAME], [AFO CODE], [POLICY TYPE], [POLICY FORM], OC, DateCreated FROM %s" table
// DEBUG.
printfn "%s" u
// DEBUG.
execNonQuery u
execNonQuery proc
Zone and DateCreated are both reserved words. It's difficult to predict when reserved words will cause trouble for SQL statements, but it seems to me that OleDb may be less forgiving, ie more likely to fail with reserved words. See if bracketing all occurrences of those names will allow the INSERT to succeed.
let u = sprintf "INSERT INTO dbo_TempTerm (UnitID, PolicyTermYear, InsuredName, PolicyNumber, RenewalDate, CovATotal, CovBTotal, CovLTotal, DwellExtn, AllOtherPerilDeductible, MedPay, TotalPremium, HurricaneDeductible, [Zone], Subzone, PercentCRC, PercentCRD, YearBuilt, RenYrs, PercentCFD, PercentHA, PercentMLD, GRP1, PercentNH, QCLM, RateV, CRI, AgentCode, AgentName, AFOCode, PolicyType, PolicyForm, OC, [DateCreated]) SELECT [UNIT ID], [POLICY TERM YEAR], [INSURED NAME], [POLICY #], [RENEWAL DT], [COV A TOTAL], [COV B TOTAL], [COV L TOTAL], [DWELL EXTN], [ALL/OTHER PERIL DEDUCTIBLE], [MED PAY], [TOT PREMIUM], [HURR DED], [ZONE], SUBZONE, [%%CRC], [%%CRD], [YR BLT], RENYRS, [%%CFD], [%%HA], [%%MLD], GRP1, [%%NH], QCLM, [RATE V], CRI, [AGENT CODE], [AGENT NAME], [AFO CODE], [POLICY TYPE], [POLICY FORM], OC, [DateCreated] FROM %s" table

How to read a NSInputStream with UTF-8?

I try to read a large file in iOS using NSInputStream to separate the files line by newlines (I don't want to use componentsSeparatedByCharactersInSet as it uses too much memory).
But as not all lines seem to be UTF-8 encoded (as they can appear just as ASCII, same bytes) I often get the Incorrect NSStringEncoding value 0x0000 detected. Assuming NSASCIIStringEncoding. Will stop this compatiblity mapping behavior in the near future. warning.
My question is: Is there a way to surpress this warning by e.g. setting a compiler flag?
Furthermore: Is it save to append/concatenate two buffer reads, as reading from the byte stream, then converting the buffer to string and then appending the string could make the string corrupted?
Below an example method that demonstrates that the byte to string conversion will discard the first and second half of the UTF-8 character, as being invalid.
- (void)NSInputStreamTest {
uint8_t testString[] = {0xd0, 0x91}; // #"Б"
// Test 1: Read max 1 byte at a time of UTF-8 string
uint8_t buf1[1], buf2[1];
NSString *s1, *s2, *s3;
NSInteger c1, c2;
NSInputStream *inStream = [[NSInputStream alloc] initWithData:[[NSData alloc] initWithBytes:testString length:2]];
[inStream open];
c1 = [inStream read:buf1 maxLength:1];
s1 = [[NSString alloc] initWithBytes:buf1 length:1 encoding:NSUTF8StringEncoding];
NSLog(#"Test 1: Read %d byte(s): %#", c1, s1);
c2 = [inStream read:buf2 maxLength:1];
s2 = [[NSString alloc] initWithBytes:buf2 length:1 encoding:NSUTF8StringEncoding];
NSLog(#"Test 1: Read %d byte(s): %#", c2, s2);
s3 = [s1 stringByAppendingString:s2];
NSLog(#"Test 1: Concatenated: %#", s3);
[inStream close];
// Test 2: Read max 2 bytes at a time of UTF-8 string
uint8_t buf4[2];
NSString *s4;
NSInteger c4;
NSInputStream *inStream2 = [[NSInputStream alloc] initWithData:[[NSData alloc] initWithBytes:testString length:2]];
[inStream2 open];
c4 = [inStream2 read:buf4 maxLength:2];
s4 = [[NSString alloc] initWithBytes:buf4 length:2 encoding:NSUTF8StringEncoding];
NSLog(#"Test 2: Read %d byte(s): %#", c4, s4);
[inStream2 close];
}
Output:
2013-02-10 21:16:23.412 Test[11144:c07] Test 1: Read 1 byte(s): (null)
2013-02-10 21:16:23.413 Test[11144:c07] Test 1: Read 1 byte(s): (null)
2013-02-10 21:16:23.413 Test[11144:c07] Test 1: Concatenated: (null)
2013-02-10 21:16:23.413 Test[11144:c07] Test 2: Read 2 byte(s): Б
First of all, in line: s3 = [s1 stringByAppendingString:s2]; you are trying to concatenate to 'nil' values. The result would be 'nil' also. So, you may want to concatenate bytes instead of strings:
uint8_t buf3[2];
buf3[0] = buf1[0];
buf3[1] = buf2[0];
s3 = [[NSString alloc] initWithBytes:buf3 length:2 encoding:NSUTF8StringEncoding];
Output:
2015-11-06 12:57:40.304 Test[10803:883182] Test 1: Read 1 byte(s): (null)
2015-11-06 12:57:40.305 Test[10803:883182] Test 1: Read 1 byte(s): (null)
2015-11-06 12:57:40.305 Test[10803:883182] Test 1: Concatenated: Б
Secondary, length of UTF-8 character may lay in [1..6] bytes.
(1 byte) 0aaa aaaa //if symbol lays in 0x00 .. 0x7F (ASCII)
(2 bytes) 110x xxxx 10xx xxxx
(3 bytes) 1110 xxxx 10xx xxxx 10xx xxxx
(4 bytes) 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
(5 bytes) 1111 10xx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx
(6 bytes) 1111 110x 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx
So, if you are intended to read from NSInputStream raw bytes and then translate them into UTF-8 NSString, you probably want to read byte by byte from NSInputStream until you will get valid string:
#define MAX_UTF8_BYTES 6
NSString *utf8String;
NSMutableData *_data = [[NSMutableData alloc] init]; //for easy 'appending' bytes
int bytes_read = 0;
while (!utf8String) {
if (bytes_read > MAX_UTF8_BYTES) {
NSLog(#"Can't decode input byte array into UTF8.");
return;
}
else {
uint8_t byte[1];
[_inputStream read:byte maxLength:1];
[_data appendBytes:byte length:1];
utf8String = [NSString stringWithUTF8String:[_data bytes]];
bytes_read++;
}
}
ASCII (and hence the newline character) is a subset of UTF-8, so there should not be any conflict.
It should be possible to divide your stream at the newline characters, as you would in a simple ASCII stream. Then you can convert each chunk ("line") into an NSString using UTF-8.
Are you sure the encoding errors are not real, i.e., that your stream may actually contain erroneous characters with respect to a UTF-8 encoding?
Edited to add from the comments:
This presumes that the lines consist of sufficiently few characters to keep a whole line in memory before converting from UTF-8.

Resources