mutable VS immutable

mutable VS immutable

可变 与 不可变 类别

Objective-C在Fundation Framework中提供了 可变类别(mutable)不可变类别(immutable),依字面来说,不可变类别即是物件宣告时就给予初始值内容,其内容无法去修改,如果用方法所产生与原先不同的内容时,其方法都会将物件重新建立后再取代原先物件。

不可变类别

举个例子来说,Java在字串中也有 StringStringBuilder 这两种方式,以 StringBuilder 就是指可变类别(mutable),下面以NSString例子来看一下不可变类别:

//-----------start-----------
    NSString *str = @"danny";
    NSLog(@"%@",str);
    str = [str stringByAppendingString:@" is danny"];
    NSLog(@"%@",str);
//------------end------------

程式中看的出来第一次初始值为danny,之后再将它加入is danny,但在不可变的底层来说:

  • 第一次建立 danny 指向str
  • 第二次建立 danny is danny 指向str并删除第一次建立的danny

所以就会是这样:

//-----------start-----------
    NSString *str = @"danny";
    NSLog(@"%@",str);
    str = @"danny is danny";
    NSLog(@"%@",str);
//------------end------------

要如何证明呢?

//-----------start-----------
    NSString *str1 = @"This is string A";
    NSString *res;

    res=str1;

    str1 = [str1 stringByAppendingString:@" This is string B"];

    NSLog(@"res=%@",res);
    NSLog(@"str1=%@",str1);

//------------end------------

如果res等于str1那接下来如果增加str1的内容时如果是同一个记忆体,那印出这2个变数值应该会相同,但印出来的结果却:

2014-02-20 18:06:27.694 test1[46806:303] res=This is string A
2014-02-20 18:06:27.696 test1[46806:303] str1=This is string A This is string B

这就可以证实在处理时它会重新建立新的物件,造成记忆参考的位址是不相同的。

可变类别

依照前面的做法,同样要依序两次加入字串dannydanny is danny可变类别程式则是:

//-----------start-----------
    NSMutableString *mstr = [NSMutableString stringWithString:@"danny"];
    NSLog(@"%@",mstr);
    [mstr appendString:@" is danny"];
    NSLog(@"%@",mstr);
//------------end------------

可变类别会将2次的字串利用链结的方式将2个字串绑定:

  • 第一次建立 danny 指向mstr
  • 第二次建立 is danny 并与 danny 都指向mstr
  • 最终mstr为 danny + is danny

在记忆体分配来说,会先分配一组danny,再分配第二组is danny,当我们要印出来时就会根据链结关系将完整的字串印出来。 这里要证明它加入新的字串内容时,参考的记忆体位址是一样的:

//-----------start-----------
    NSMutableString *str1 = [NSMutableString stringWithString:@"This is string A"];
    NSMutableString *res;
    res=str1;

    [str1 appendString:@" This is string B"];

    NSLog(@"res=%@",res);
    NSLog(@"str1=%@",str1);
//------------end------------

输出结果: 2014-02-20 18:49:07.174 test1[47236:303] res=This is string A This is string B 2014-02-20 18:49:07.176 test1[47236:303] str1=This is string A This is string B

印出resstr1时,其内容都是最后被修改的内容,如此可证明记忆体的参考点是相同的,增加字串内容只是将加入的字串做个链结。

结论

以上这两种方式利用范例将它表示出来,那这两个究竟是差在哪呢?整体来说在使用上是没差别,但以效率而言,不可变类别效率会比可变类别来的好!为什么呢?因为不可变类别在建立时,会跟系统要一个静态的记忆体位址,所以字串在存放时是连续的,只要从头存放到尾,但可变类别却是很多块的记忆体位址所组合成的存放空间,在使用上时必需还要加上参考资料才能将完整的字串呈现,在运用上就没有不可变类别有效率。