@property
- @property 其实就是在编译阶段由编译器自动帮我们生成ivar成员变量getter方法,setter方法。
使用“自动合成”( auto synthesis)这个过程由编译器在编译阶段执行自动合成,所以编译器里看不到这些“合成方法”(synthesized method)的源代码。除了生成getter、setter方法之外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此作为实例变量的名字)
每次增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在 prop_list 中增加一个属性的描述,计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现。
在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。
下面看看内部代码:
@property(nonatomic,strong)UIButton * testBtn;
@property(nonatomic,strong)NSMutableArray * testArray;
@end
@implementation testBaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
unsigned int count , methodC;
Ivar * ivars = class_copyIvarList([self class], &count);
for (int i =0 ; i < count; i++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
NSLog(@"成员变量:%s",name);
}
free(ivars);
Method * methods = class_copyMethodList([self class], &methodC);
for (int i =0 ; i < methodC; i++) {
Method ivar = methods[i];
struct objc_method_description * des = method_getDescription(ivar);
NSLog(@"%@",NSStringFromSelector(des->name));
}
}
2017-07-17 10:41:16.639 LeeSDWebImageLearn[1143:59316] 成员变量:_testBtn
2017-07-17 10:41:16.639 LeeSDWebImageLearn[1143:59316] 成员变量:_testArray
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] testBtn
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] setTestBtn:
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] testArray
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] setTestArray:
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] .cxx_destruct
2017-07-17 10:41:16.641 LeeSDWebImageLearn[1143:59316] viewDidLoad
注意:
在声明property属性后,有2种实现选择
@synthesize (Xcode6以后省略这个了, 默认在 @implementation .m中添加这个@synthesize xxx = _xxx; )
编译器期间,让编译器自动生成getter/setter方法。
但是当有自定义的存或取方法时,自定义会屏蔽自动生成该方法@dynamic (Xcode6以后省略这个了, 默认在 @implementation .m中添加这个@synthesize xxx; )
就是要来告诉编译器,代码中用@dynamic修饰的属性,其getter和setter方法会在程序运行的时候或者用其他方式动态绑定,以便让编译器通过编译。
使用:
@interface testBaseViewController (){
UIButton * _heBtn;//使用@dynamic 要自己声明一个成员变量自己实现setter/getter 方法
}
@property(nonatomic,copy)NSString * name;
@property(strong)UIButton * clickBtn;
@property(nonatomic,strong)UIButton * heBtn;
@end
@implementation testBaseViewController
@synthesize name = _name;
@synthesize clickBtn = _clickBtn;
@dynamic heBtn ;
-(void)setName:(NSString *)name{
if (_name != name) {
_name = name;
}
}
-(NSString*)name{
return _name;
}
-(void)setClickBtn:(UIButton *)clickBtn{
@synchronized (self) {
if (_clickBtn != clickBtn) {
_clickBtn = clickBtn;
}
}
}
-(UIButton*)clickBtn{
@synchronized (self) {
return _clickBtn;
}
}
-(void)setHeBtn:(UIButton *)heBtn{
if (_heBtn != heBtn) {
_heBtn = heBtn;
}
}
-(UIButton*)heBtn{
return _heBtn;
}
readwrite,readonly,assign,retain,copy,nonatomic,atomic,strong,weak属性的作用分别是什么。
- readwrite
此标记说明属性会被当成读写的,这也是默认属性。 - readonly
此标记说明属性只可以读,也就是不能设置,可以获取。 - assign
不会使引用计数加1,也就是直接赋值,一般用于基本数据类型。 - retain
会使引用计数加1,先release 旧值,在retain新值。mrc 下使用。 - copy
建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝。 - nonatomic
非原子性访问,多线程并发访问会提高性能。 - atomic
原子性访问。资源加锁,线程同步访问。 - strong
打开ARC时才会使用,相当于retain。 - weak
打开ARC时才会使用,相当于assign,可以把对应的指针变量置为nil。
分析:
atomic
系统生成的getter/setter方法会进行加锁操作,注意:这个锁仅仅保证了getter和setter存取方法的线程安全.
因为getter/setter方法有加锁的缘故,故在别的线程来读写这个属性之前,会先执行完当前操作.
atomic是线程安全的吗?
不是,
很多文章谈到atomic和nonatomic的区别时,都说atomic是线程安全,其实这个说法是不准确的.
atomic只是对属性的getter/setter方法进行了加锁操作,这种安全仅仅是set/get 的读写安全,并非真正意义上的线程安全,因为线程安全还有读写之外的其他操作(比如:如果当一个线程正在get或set时,又有另一个线程同时在进行release操作,可能会直接crash)
nonatomic
系统生成的getter/setter方法没有加锁,线程不安全,但更快
当多个线程同时访问同一个属性,会出现无法预料的结果
下面看演示:
@property(nonatomic,copy)NSString * name;
@property(strong)UIButton * clickBtn;
@end
@implementation testBaseViewController
@synthesize name = _name;
@synthesize clickBtn = _clickBtn;
- nonatomic对象setter和getter方法的实现
-(void)setName:(NSString *)name{
if (_name != name) {
_name = name;
}
}
-(NSString*)name{
return _name;
}
- atomic对象setter和getter方法的实现
-(void)setClickBtn:(UIButton *)clickBtn{
@synchronized (self) {
if (_clickBtn != clickBtn) {
_clickBtn = clickBtn;
}
}
}
-(UIButton*)clickBtn{
@synchronized (self) {
return _clickBtn;
}
}
weak assign
3.引用计数的工作原理:
- 当我们创建(alloc)一个新对象A的时候,它的引用计数从零变为 1;
- 当有一个指针指向这个对象A,也就是某对象想通过引用保留(retain)该对象A时,引用计数加 1;
- 当某个指针/对象不再指向这个对象A,也就是释放(release)该引用后,我们将其引用计数减 1;
- 当对象A的引用计数变为 0 时,说明这个对象不再被任何指针指向(引用)了,这个时候我们就可以将对象A销毁,所占内存将被回收,且所有指向该对象的引用也都变得无效了。系统也会将其占用的内存标记为“可重用”(reuse);
在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性
自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong
weak 和 assign 和 strong 的不同点:
- assign:用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
- weak:对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
- strong:对对象的强引用,增加对象的引用计数,持有对象。
- weak 策略在属性所指的对象遭到摧毁时,系统会将 weak 修饰的属性对象的指针指向 nil,在 OC 给 nil 指针对象发消息是不会有什么问题的;如果使用 assign 策略在属性所指的对象遭到摧毁时,对象的内存区块被擦写,原有的各种方法的内存指向没有了,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,当前指针所指向的内存区块就没有那些方法了,很容造成程序奔溃,assigin 可以用于修饰非 OC 对象,而 weak 必须用于 OC 对象。
- Delegate基本总是使用weak,以防止循环引用。特殊情况是,希望在dealloc中调用delegate的某些方法进行释放,此时如果使用weak将引起异常,因为此时已经是nil了,那么采用assign更为合适。
UIButton * testBtn = [UIButton new];
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
self.strongBtn = testBtn;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
self.strongBtn1 = testBtn;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
self.assignBtn = testBtn;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
self.weakBtn = testBtn;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
[self.view addSubview:testBtn];
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
self.strongBtn = nil;
self.strongBtn1 = nil;
self.assignBtn = nil;
self.weakBtn = nil;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
上面分别使用strong assign weak 引用btn对象结果只有strong 和 addsubview 导致原始对象的引用计数增加了,其他的没变。
2017-07-17 16:19:02.024 LeeSDWebImageLearn[2302:176043] 引用计数:1
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:2
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:3
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:3
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:3
2017-07-17 16:19:02.026 LeeSDWebImageLearn[2302:176043] 引用计数:4
2017-07-17 16:19:02.026 LeeSDWebImageLearn[2302:176043] 引用计数:2
weak 实现原理的概括
Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。
- weak 的实现原理可以概括一下三步:
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
copy
首先以一个String类型 demo分析吧:
@property(nonatomic,strong)NSString * bigName;//这里设置成strong
@property(nonatomic,strong)NSString * smallName;
@property(nonatomic,copy)NSString * firstName;//这里设置成copy
@property(nonatomic,copy)NSString * lastName;
调用的地方:
NSMutableString * name1 = [NSMutableString stringWithString:@"oliverlee"];
NSString * name2 = @"liyangyang";
Person *p = [[Person alloc] init];
p.bigName = name1;
p.smallName = name2;
p.firstName = name1;
p.lastName = name2;
NSLog(@"big=%@,small=%@,first=%@,last=%@",p.bigName,p.smallName,p.firstName,p.lastName);
[name1 appendString:@"hello world"];
name2 = @"world hello";
NSLog(@"big=%@,small=%@,first=%@,last=%@",p.bigName,p.smallName,p.firstName,p.lastName);
看控制台:
2017-07-17 18:29:03.759 LeeSDWebImageLearn[2896:235465] big=oliverlee,small=liyangyang,first=oliverlee,last=liyangyang
2017-07-17 18:29:03.760 LeeSDWebImageLearn[2896:235465] big=oliverleehello world,small=liyangyang,first=oliverlee,last=liyangyang
这次看出了我们平时同学乱使用的strong出问题了吧,当你用可变的字符串赋值后,在修改原来的字符串导致结果变化了,而用copy修饰的无论是可变还是不可变都不影响结果。
当我们通过一个 NSMutableString 对 NSString 变量进行赋值,如果 NSString 的 property 是 strong 类型的时候,就会随着 NSMutableString 类型的变量一起变化。
NSString * string = @"test";
NSString * stringCopy = [string copy];//内存地址没有变化,不可变。
NSMutableString * stringMCopy = [string mutableCopy];//内存地址发生变化,拷贝了值和地址,可变的
[stringMCopy appendString:@"hello"];
NSLog(@"%p,%p,%p------->%@,%@,%@",string,stringCopy,stringMCopy,string,stringCopy,stringMCopy);
NSMutableString * mutString = [NSMutableString stringWithString:@"test"];
NSMutableString * m_stringCopy = [mutString copy];//内存地址发生变化,值也拷贝了,但是不可变
NSMutableString * m_stringMutCopy = [mutString mutableCopy];//内存地址和值都拷贝了,可变的。
// [m_stringCopy appendString:@"hello"];
[m_stringMutCopy appendString:@"oliver"];
NSLog(@"%p,%p,%p------->%@,%@,%@",mutString,m_stringCopy,m_stringMutCopy,mutString,m_stringCopy,m_stringMutCopy);
NSString * str1 = @"123";
NSString * str2 = @"123";
NSMutableString * mstr1= [NSMutableString stringWithString:@"123"];
NSMutableString * mstr2 = [NSMutableString stringWithString:@"123"];
NSLog(@"%p,%p,%p,%p",str1,str2,mstr1,mstr2);
2017-07-17 21:25:56.657 LeeSDWebImageLearn[3588:303863] 0x10fc39bd0,0x10fc39bd0,0x60800007ad80,0x60800007ad40
从上面看出NSString 类型的字符串放在了常量区也就是静态区,NSMutableString是在堆区
- NSString 类型采用copy方法,内存地址没发生变化,只是指向了原来的对象,认为不可变类型(浅拷贝)
- NSString 类型采用mutablecopy方法,内存地址发生变化,拷贝了值换了地址,可变的操作 (深拷贝)
- NSMutableString 类型采用copy 方法,内存地址发生变化,值也拷贝了,但是不可变操作 (浅拷贝)
- NSMutableString 类型采用mutablecopy方法,内存地址发生改变,值也拷贝了,是可变的操作 (深拷贝)
**2. 系统的容器类对象 **
指NSArray,NSMutableArray,NSDictionary,NSMutableDictionary等。对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。
- 看下NSArray 和 NSDictioary 的copy 和 mutablecopy
NSArray * array = @[@"hello"];
NSMutableArray * copyArray = array.copy;
NSLog(@"%@",[copyArray class]);
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
NSMutableArray * muCopyArray = array.mutableCopy;
[muCopyArray addObject:@"world"];
NSLog(@"element::::::%p----%p----%p",array[0],muCopyArray[0],copyArray[0]);
NSLog(@"%p,%p,%p------->%@,%@,%@",array,copyArray,muCopyArray,array,copyArray,muCopyArray);
copyArray = nil;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
NSDictionary * dic = @{@"key":@"hello"};
NSMutableDictionary * copyDic = dic.copy;
NSLog(@"%@",[copyDic class]);
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));
NSMutableDictionary * mutDic = dic.mutableCopy;
[mutDic setValue:@"oliver" forKey:@"name"];
NSLog(@"element::::::%p----%p----%p",dic[@"key"],mutDic[@"key"],copyDic[@"key"]);
NSLog(@"%p,%p,%p------->%@,%@,%@",dic,copyDic,mutDic,dic,copyDic,mutDic);
copyDic = nil;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));
控制台输出:
2017-07-17 23:19:39.853 LeeSDWebImageLearn[4068:350869] __NSSingleObjectArrayI
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] 引用计数:2
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] element::::::0x10e431650----0x10e431650----0x10e431650
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] 0x608000019cb0,0x608000019cb0,0x60000004cf30------->(
hello
),(
hello
),(
hello,
world
)
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] 引用计数:1
2017-07-17 23:19:39.855 LeeSDWebImageLearn[4068:350869] __NSSingleEntryDictionaryI
2017-07-17 23:19:39.855 LeeSDWebImageLearn[4068:350869] 引用计数:2
2017-07-17 23:19:39.855 LeeSDWebImageLearn[4068:350869] element::::::0x10e431650----0x10e431650----0x10e431650
2017-07-17 23:19:39.856 LeeSDWebImageLearn[4068:350869] 0x6080000362c0,0x6080000362c0,0x60800004acb0------->{
key = hello;
},{
key = hello;
},{
key = hello;
name = oliver;
}
2017-07-17 23:19:39.856 LeeSDWebImageLearn[4068:350869] 引用计数:1
- 看下NSMutableArray 和 NSMutableDictionary 的copy 和 mutablecopy
NSMutableArray * array = @[@"hello"].mutableCopy;
NSMutableArray * copyArray = array.copy;
NSLog(@"%@",[copyArray class]);
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
NSMutableArray * muCopyArray = array.mutableCopy;
[muCopyArray addObject:@"world"];
NSLog(@"element::::::%p----%p----%p",array[0],muCopyArray[0],copyArray[0]);
NSLog(@"%p,%p,%p------->%@,%@,%@",array,copyArray,muCopyArray,array,copyArray,muCopyArray);
copyArray = nil;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
NSMutableDictionary * dic = @{@"key":@"hello"}.mutableCopy;
NSMutableDictionary * copyDic = dic.copy;
NSLog(@"%@",[copyDic class]);
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));
NSMutableDictionary * mutDic = dic.mutableCopy;
[mutDic setValue:@"oliver" forKey:@"name"];
NSLog(@"element::::::%p----%p----%p",dic[@"key"],mutDic[@"key"],copyDic[@"key"]);
NSLog(@"%p,%p,%p------->%@,%@,%@",dic,copyDic,mutDic,dic,copyDic,mutDic);
copyDic = nil;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));
控制台输出:
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] __NSSingleObjectArrayI
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] 引用计数:1
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] element::::::0x110036650----0x110036650----0x110036650
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] 0x600000054970,0x60000000d600,0x600000054af0------->(
hello
),(
hello
),(
hello,
world
)
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] 引用计数:1
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] __NSDictionaryI
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] 引用计数:1
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] element::::::0x110036650----0x110036650----0x110036650
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] 0x6080000510a0,0x60800007acc0,0x608000054640------->{
key = hello;
},{
key = hello;
},{
key = hello;
name = oliver;
}
2017-07-17 22:06:19.121 LeeSDWebImageLearn[3890:322501] 引用计数:1
分析如下:
- 不可变的数组和字典进行copy 操作时候,引用计数为增加,但是内存地址不变,属于共用元素,(浅拷贝)
- 不可变的数组和字典进行mutablecopy 操作时候,引用计数为不变,但是内存地址改变,元素的地址依然指向最初的对象的内存地址,可以改变(伪深拷贝)
- 可变的数组和字典进行copy 操作时候,引用计数为不变,但是内存地址改变只是对象的内存地址,元素的地址依然指向最初的对象的内存地址,猜想可能是为了节省内存空间吧,不可变(伪深拷贝)
- 可变的数组和字典进行mutablecopy 操作时候,引用计数为不变,但是内存地址改变,元素的地址依然指向最初的对象的内存地址,重点可以改变(伪深拷贝)
回过头来开属性关键字
@property(nonatomic,copy)NSString * c_str;
@property(nonatomic,strong)NSMutableString * c_mstr;
@property(nonatomic,copy)NSArray * c_array;
@property(nonatomic,strong)NSMutableArray * c_marray;
@property(nonatomic,copy)NSDictionary * c_dic;
@property(nonatomic,strong)NSMutableDictionary * c_mdic;
NSString * teststr = @"good";
NSArray * testarr = @[@"test"];
NSDictionary * tdic = @{@"key":@"hello"};
self.c_str = teststr;
self.c_array = testarr;
self.c_dic = tdic;
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testarr));
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)tdic));
self.c_mdic = tdic.mutableCopy;
self.c_mstr = teststr.mutableCopy;
self.c_marray = testarr.mutableCopy;
NSLog(@"%p,%p,%p",teststr,testarr,tdic);
NSLog(@"%p,%p,%p,%p,%p%p",self.c_str,self.c_mstr,self.c_array,self.c_marray,self.c_dic,self.c_mdic);
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testarr));
NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)tdic));
//都是不可变的字符串
if ([self.c_mstr respondsToSelector:@selector(appendString:)]) {
[self.c_mstr appendString:@"hello world"];
}
NSLog(@"%@",self.c_mstr);
//如果可变数组用来copy 来修饰的话将会变成不可变的程序会出问题,还是用strong来修饰吧
if ([self.c_marray respondsToSelector:@selector(removeAllObjects)]) {
[self.c_marray removeAllObjects];
}
NSLog(@"%@",testarr);
if ([self.c_mdic respondsToSelector:@selector(setValue:forKey:)]) {
[self.c_mdic setValue:@"helllo" forKey:@"888"];
}
NSLog(@"%@",self.c_mdic);
看控制台:
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 0x101fbe770,0x60800001c4f0,0x60800003bd60
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 0x101fbe770,0x60000007ecc0,0x60800001c4f0,0x600000055db0,0x60800003bd60,0x6000000590b0
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] goodhello world
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] (
test
)
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] {
888 = helllo;
key = hello;
}
从上面可以看出:
- 当修饰可变类型的属性时,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong,并且用 strong 不会导致引用计数增加,相当于内存的拷贝了。
- 当修饰不可变类型的属性时,如NSArray、NSDictionary、NSString,用copy。会导致引用计数的增加,NSString 类型除外,这个在常量区,没有引用的书法
友情链接:
http://www.cnblogs.com/iCocos/p/4462744.html
http://www.jianshu.com/p/58c985408b75
https://dayon.gitbooks.io/-ios/content/chapter2.html