initWithString和initWithFormat的区别

网上看到一篇文章,分析initWithString和initWithFormat的区别,用一下他的用例。

 
下面是测试代码:

NSString * str =[[NSString alloc] initWithString:@"this is from initWithString function"];

NSLog(@"this is from [[NSString alloc] initWithString] m_addr is %ld retainCount is %i", str, [strretainCount]);

[str release];

[str release];

[str release];

NSLog(@"this is from [[NSString alloc] initWithString] m_addr is %ld retainCount is %i", str, [strretainCount]);

 

str = [[NSString alloc] initWithFormat:@"this is from initWithFormat function"];

NSLog(@"this is from [[NSString alloc] initWithFormat] m_addr is %ld retainCount is %d", str, [strretainCount]);

 

下面是LOG的结果:

 this is from [[NSString alloc] initWithString] m_addr is 12356 retainCount is 2147483647

 this is from [[NSString alloc] initWithString] m_addr is 12356 retainCount is 2147483647

 this is from [[NSString alloc] initWithFormat] m_addr is 82076688 retainCount is 1

 

 this is from [[NSString alloc] initWithString] m_addr is 12356 retainCount is 2147483647

 this is from [[NSString alloc] initWithString] m_addr is 12356 retainCount is 2147483647

 this is from [[NSString alloc] initWithFormat] m_addr is 78748112 retainCount is 1

 

 this is from [[NSString alloc] initWithString] m_addr is 12356 retainCount is 2147483647

 this is from [[NSString alloc] initWithString] m_addr is 12356 retainCount is 2147483647

 this is from [[NSString alloc] initWithFormat] m_addr is 78777072 retainCount is 1

 

我将上面这段测试代码调用了三次,得到以上的LOG结果。顺便得出这么几条结论。对于不对,请大家鉴定。
1.从两个变量的地址看,两个变量的地址差据较大。前者的地址非常靠前。
2.从release看,前者无论被release多少次,都不会被释放,而且的值不变,而后者只要release一次,变量即消亡。
3.前者的releaseCount= NSIntegerMax,而NSIntegerMax ==INT_MAX ,而 UINT_MAX== (INT_MAX * 2U + 1U)
U是指无符号整型,而我们默认的int和NSInteger是有符号的,在32位系统中,NSIntegerMax=0X7FFFFFFF,对其乘以2U,即一次向左挪一位。结果为0XFFFFFFFE,再加1U为0XFFFFFFFF,即无符号的最大值。
然后根据苹果官方对于retainCount方法的描述:

retainCount

Returns the receiver’s reference count. (required)

- (NSUInteger)retainCount
Return Value

The receiver’s reference count.

Discussion

You might override this method in a class to implement your own reference-counting scheme. For objects that never get released (that is, their release method does nothing), this method should return UINT_MAX, as defined in <limits.h>.

小生窃以为,此处的UINT_MAX和NSIntegerMax是一样的,都是表示所在类型的最大值。所以,initWithString这个方法初始化后的对象是不可能被release的或者说,它的release方法啥也不干。

验证了上面的分析2.因为他不可能被release的或者说,它的release方法啥也不干,所以我们调用无数次release都没有起到预先想想的作用。

为什么会导致这样的情况呢?

我们再次把目光转向地址。眼尖的同学可能会看到initWithString申请的地址每次都是一样的,而initWithFormat的地址每次都不一样,这个说明什么?

说明initWithString的地址是静态的,而initWithFormat是动态的。为什么前者是静态的,而后者是动态的?

结合上面关于retainCount的分析,小生窃以为initWithString的地址申请是在编译是进行的,这样才能说明为什么它的地址空间如此靠前。只有在编译是进行的,他才是静态的。

对于initWithString生成的对象,对其进行dealloc时,程序会报错(这里就不贴LOG了)。而后者initWithFormat不会报错。这进一步验证了initWithString生成的是静态对象,而initWithFormat是动态的。

 

结论:initWithString生成的对象是在编译是申请地址空间,而且在程序中不能释放,不建议使用。(当然也有可能在某种情况下会使用到这个方法,在此不多加讨论)。


以下解释更为准确:

initWithString一个是作为常量字符串,只读数据,
[[NSString alloc]initWithString:@"this is from initWithString function"];
这段代码是返回一个不可变对象,而常量字符串本就是不可变,所以这个时候内存会通过isa优化,alloc的区域作废,对象直接指向了"this is from initWithString function"这段常量字符串存储区,作为常量字符串没有retain,所以你retain无数次还是哪个UINR_MAX
[[NSMutableString alloc]initWithString:@"this is from initWithString function"];
而这一个是返回一个可变的对象,会另申请空间存放后面的常量字符串,这时其retaincount 就为1
initWithFormat:是一编码格式读入数据,数据存放在哪是根据读入的参数确定的,跟initWithString是右区别的的;

[[NSString alloc]initWithFormat:@"dddd"];
count=1;

[[NSMutableString alloc] initWithFormat:@""];
count=1;
不管返回的是可变和不可变对象,其都会发送一个retain 和autoRelease指令
这两个结果都一样


 NSString * str1 = [[NSString alloc] initWithString:@"string 1"];
    NSLog(@"str1's address %ld, retain count %d", (long)str1, [str1 retainCount]);
    NSString * str2 = @"This is string 2";
    NSLog(@"str2's address %ld, retain count %d", (long)str2, [str2 retainCount]);
    NSString * str3 = @"string 1";
    NSLog(@"str3's address %ld, retain count %d", (long)str3, [str3 retainCount]);

,运行后输出:
2011-07-09 21:49:06.526 Ex5_push_pop_nav[1788:207] str1's address 18176, retain count 2147483647
2011-07-09 21:49:06.526 Ex5_push_pop_nav[1788:207] str2's address 18208, retain count 2147483647
2011-07-09 21:49:06.527 Ex5_push_pop_nav[1788:207] str3's address 18176, retain count 2147483647

可见,str1和str3是在复用内存地址的。或者可以说[[NSString alloc] initWithString:@"string 1"]; 等价于@"string 1";

 

所以说只要你代码中写了 
NSString * str3 = @"string 1";
实际上就会在程序的静态变量区存在一个值为 ”string 1"的字符串常量。
而str3只是指向这个常量。因为这是一个常量,所以它的retainCount是MAX_INT
多次值为"string 1“的变量产生,都会指向这一个常量的地址。

如果你把代码写成:
str = [[NSString alloc] initWithFormat:@"this is from initWithFormat function"];
实际上是把常量区的复制了一份在堆中,所以你可以release了。但是 @"this is from initWithFormat function" 这个变量本身还是存放在常量区了,所以并没有省内存,反而是增加了内存的使用。

所以,如果直接用一个字符串产生另一个字符串,尽量少用 initWithFormat,而不是initWithString。

而且,从功能上来说,initWithFormat可以支持 %d 等格式化字串,所以功能更强大一些,但是缺点就是产生了额外的内存空间。

posted @ 2013-09-06 14:02  duger  阅读(1382)  评论(0编辑  收藏  举报