NSString 与 NSMutableString
NSString是不可变
字符串对象,这句话的意思,结合代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) { @autoreleasepool { NSString *str = @"Shaw"; NSString *str1 = @"Root"; // NSString *str1的意思是str1指向的@"Root"对象是不可变的,但str1是可以改变指向的。 NSLog(@"str = %@, str1 = %@",str,str1); NSLog(@"str:%p, str1:%p",str,str1); str = [str stringByAppendingString:@"andRoot"]; // 打印可以看到str地址变了,因为原地址下的对象是不可变的。 NSLog(@"str:%p, str1:%p",str,str1); str1 = str; // 使str1指向str对象 NSLog(@"str = %@, str1 = %@",str,str1); NSLog(@"str:%p, str1:%p",str,str1); } return 0; } // 输出结果 2016-04-06 13:32:45.320 test[40011:4790098] str = Shaw, str1 = Root 2016-04-06 13:32:45.321 test[40011:4790098] str:0x100001050, str1:0x100001070 2016-04-06 13:32:45.321 test[40011:4790098] str:0x1001026b0, str1:0x100001070 2016-04-06 13:32:45.321 test[40011:4790098] str = ShawandRoot, str1 = ShawandRoot 2016-04-06 13:32:45.321 test[40011:4790098] str:0x1001026b0, str1:0x1001026b0 Program ended with exit code: 0
|
同理,NSMutableString
就是可变
字符串对象。
stringByAppendingString:
方法的定义为
1
| - (NSString *)stringByAppendingString:(NSString *)aString;
|
1 2 3 4 5 6 7 8 9
| NSMutableString *mStr = [NSMutableString stringWithString:@"Shaw"]; NSLog(@"%@ %p",mStr,mStr); [mStr appendString:@"andRoot"]; // 可以看到输出地址为同一个,即对当前对象做了改变。 NSLog(@"%@ %p",mStr,mStr);
// 输出结果 2016-04-06 16:17:05.789 test[40118:4806417] Shaw 0x100203470 2016-04-06 16:17:05.790 test[40118:4806417] ShawandRoot 0x100203470 Program ended with exit code: 0
|
如果用NSMutableString
对象调用stringByAppendingString:
方法会出现警告"Incompatible pointer types assigning NSMutableString to NSString"
mutableCopy(遵从NSMutableCopying
协议的对象可用) 与 copy (遵从NSCopying
协议的对象可用)
mutableCopy
返回的对象是可变
的, copy
返回的是不可变
的。
所以用copy
返回的字符串是NSString *
, mutableCopy
返回的字符串是NSMutableString *
.
复制不可变对象时
复制可变对象时
mutableCopy
与copy
都是深复制,但copy
返回的对象不可变。这个很好理解,因为copy
返回的是不可变对象
,那么当用copy
复制一个可变对象
,复制前可变
,复制后不可变
,当然得另开空间。
下面可以用代码一一验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // 复制不可变对象 NSString *str = @"Shaw"; NSString *strCopy = [str copy]; // 相当于 [str retain] NSMutableString *strMCopy = [str mutableCopy];
NSLog(@"%@:%p %@:%p %@:%p",str,str,strCopy,strCopy,strMCopy,strMCopy); strCopy = [str stringByAppendingString:@"andRoot"]; strMCopy = [strMCopy stringByAppendingString:@"andRoot"]; NSLog(@"%@:%p %@:%p %@:%p",str,str,strCopy,strCopy,strMCopy,strMCopy);
// 输出结果 2016-04-06 18:06:17.399 test[40355:4843127] Shaw:0x100001050 Shaw:0x100001050 Shaw:0x100600380 2016-04-06 18:06:17.400 test[40355:4843127] Shaw:0x100001050 ShawandRoot:0x100103870 Shaw:0x100600380
|
用copy
方法复制NSString
对象, 出于性能原因, 既然二者本身都不可变,那么不如直接返回源对象,所以二者返回地址一样,所以是浅复制,也就相当于对源对象做retain
.
因为strCopy
是NSString
对象,向其发送stringByAppendingString:
消息,是开辟了另一块空间(0x100103870)
的,并没有改变之前那块空间(0x100001050)
的对象.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| // 复制可变对象
NSMutableString *mStr = [NSMutableString stringWithString:@"Shaw"]; NSMutableString *strMCopy = [mStr mutableCopy]; NSString *strCopy = [mStr copy];
// NSLog(@"%@:%p %@:%p %@:%p",mStr,mStr,strMCopy,strMCopy,strCopy,strCopy);
NSString *mStrCopy = [mStr copy]; // 打印结果可以看出mStrCopy和strCopy其实是同一个对象,也就是说可变对象的copy只能新开一块空间出来,之后再copy也都是指向这块空间的对象。
NSLog(@"%@:%p %@:%p %@:%p %@:%p",mStr,mStr,strMCopy,strMCopy,strCopy,strCopy,mStrCopy,mStrCopy);
strMCopy = [strMCopy stringByAppendingString:@"andRoot"]; // 会放在新地址 strCopy = [strCopy stringByAppendingString:@"andRoot"]; // 会放在新地址
NSLog(@"%@:%p %@:%p %@:%p",mStr,mStr,strMCopy,strMCopy,strCopy,strCopy);
// 输出结果
2016-04-06 19:15:09.413 test[40489:4866634] Shaw:0x100400290 Shaw:0x1004004d0 Shaw:0x7761685345 Shaw:0x7761685345 2016-04-06 19:15:09.414 test[40489:4866634] Shaw:0x100400290 ShawandRoot:0x100400900 ShawandRoot:0x100400550
|
为什么NSString对象在@property属性声明时写的是copy?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // 假设Person类中有声明如下 // .h
@property (nonatomic, copy) NSString *name;
// main.m
Person *per = [Person alloc] init]; NSMutableString *mStr = [NSMutableString stringWithFormat:@"Root"]; per.name = mStr; NSLog(@"name is %@ now", per.name); [mStr appendString:@"andShaw"]; NSLog(@"name is %@ now ", per.name);
// 输出结果会是这样 name is Root now name is Root now
|
而将copy改为retain
输出结果会是
name is Root now
name is RootandShaw now
set方法的例子
1 2 3 4 5 6 7
| setName:(NSString *)name { if (_name != name) { [_name release]; _name = [name retain]; // copy时此处将retain换成copy } }
|
小结
复制方法存在的目的就是为了复制出一个当对它做出改变而不会影响源对象的对象.
当然如果想改变一个NSString对象也不是不可以
比如
1 2 3 4 5 6 7 8
| NSString *str = @"Shaw"; NSLog("str = %@, addr = %p", str, str); NSString *__strong *p = &str; *p = @"Root"; NSLog("str = %@, addr = %p", str, str);
// 输出结果在同一个地址,且已经改变
|
至于__strong
, 是对象ownership
的话题了.
这方面的知识我是在Objective-C高级编程:iOS与OS X多线程和内存管理上获取的。
weak
strong
是ARC
引入的关键词
1
| NSString *firstName = @"Ray";
|
firstName对@”Ray”强引用
此时猜想一个textField,当输入Ray
1
| self.textField.text = @"Ray";
|
此时有两个strong指针指向此对象
textField中的文字变化 变成Rayman
此时就变成下图的样子
只有当firstName被赋予新值,或者含有此局部变量的方法结束,或者因为firstName是一个实例变量且它所属的那个对象已经deallocated,这种所有权才结束。
当@”Ray”不再被任何强指针拥有,它就被释放了。
把firstName和self.textField.text这种指针称为strong指针,因为它们使对象存在于内存中。
默认情况下的实例变量,局部变量都是强指针。
weak
1
| __weak NSString *weakName = self.textField.text;
|
weak
指针是需要显式声明的,用__weak
关键字
weakName指向对象但并没有拥有它,如果textField内容发生变动,那么@”Rayman”对象不再被任何强指针指向,它会被释放掉。
weak比assign多了一个作用就是当它指向的对象已经被销毁了,它会自己置成nil。
这是非常方便的,因为这防止弱指针继续指向那片已经被释放了的空间,曾经因为这个问题造成了很多bug,你也许有听过”悬摆指针””野指针”,但是有了weak指针,这些问题不会出现了。
weak指针多数被用到有父子关系的两个对象上,父对象用strong指向子对象,子对象用weak指向父对象,这样就避免了内存循环,下面是一个例子
如果是从storyboard
中连了线到代码块的,那都是添加到了父视图的子视图树里的,也就是说视图是有父对象的强指针指向的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // // ViewController.h // test2 // // Created by Shaw on 16/4/6. // Copyright © 2016年 Shaw. All rights reserved. //
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic,weak) UIScrollView *scrollView;
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 353, 400, 200)]; _scrollView = scrollView; [self.view addSubview:self.scrollView];
}
|
这几句代码可以用下图来描述
担心scrollView
对象有两个强指针指向不好释放?
scrollView
变量是个局部变量,出了那个方法,就被释放掉了,之后就只有self
一个强指针指向它了。
retain
retain
在ARC
下是不能显式写的,但是在@property(nonatomic,retain)
这样写是没问题的。
retain
的属性的setter
是先release
旧值,再retain
新值
1 2 3
| @property (nonatomic,retain) NSString *string; // 当赋给string属性的对象总是NSString *,那么用retain和copy都是一样的
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| -(void)setString:(NSString *)str{
// if(str == _string){
// return;
// }
[_string release];
_string = [str retain];
}
|
1
| ** copy retain assign的差别事实上就是在于对象属性的"set"方法 **
|