首先来聊聊内存管理。因为是先有了内存管理这个东西,才慢慢的有了ARC,而后才会有@property的各种属性
聊到内存管理,我们就能知道iOS5之前,iOS的内存管理是MRC(手动内存管理)的。iOS5之后才有了ARC(自动内存管理)。
那我们就来看看MRC是怎么做的:
在iOS中,每创建一个对象,对象的RC(引用计数)都是自动为1。
MRC中,对象调用retain时,RC就会+1;对象调用release时,RC就会-1;当RC的值为0时,系统会调用dealloc方法来销毁对象。
所以MRC下的set 和 get方法是这样的:
@property (nonatomic, retain) NSString * name;
- (void) setter:(NSString *)name
{
if(_name != name){
[_name release];
_name = [name retain];//或者copy
}
}
-(NSString *)name{
if(!_name){
_name = @"xxx";
}
return [[_name retain] autorelease];
}
在MRC下,@property关键字有:(这里的使用都不是绝对的,只是一种选择策略,比如NSString也可以用retain,要据情况而定)
1:assign、retain 和 copy:copy只用于NSString/block;retain用于除NSString/block以外的OC对象;assign用于非OC对象(基本数据类型、枚举等),当两个对象相互引用的时候,一个要用retain,一个用assign。之所以NSString使用copy,就是因为NSMutableString的对象可以赋值给NSString,而父类指针可以指向子类对象,所以给这个属性赋值的时候,无论是传入可变对象还是不可变对象,那它都会是不可变的副本。如果使用strong,而传入的对象是可变对象,那如果外部修改了这个可变对象,则就会修改到该属性值。同理,NSArray、NSDictionary也应该使用copy。(copy会分配新的内存空间,retain或下面的strong只是对引用计数加一)
2:nonatomic 和 atomic:atomic的意思是原子的,它是指属性存储是线程安全(一次只有一个对象对它操作)的,但是atomic不一定就绝对线程安全了,比如一个可变数组arr是线程安全的(atomic),但是[arr objectAtIndex:index]就不是线程安全的了。
3:readwrite 和 readonly:(这个就很好理解了)
4:getter 和 setter
ARC下新加了以下几种@property属性:
1:strong 与MRC下的retain是类似。引用计数+1
2: weak, unsafe_unretained这两个与assign差不多,不过也有区别,等下介绍;另外ARC下使用assign的概率也是非常高的。
总结起来就是:内存管理基本是对引用计数的操作。对于property关键字,在MRC下,OC对象通常使用retain,非OC对象使用assign,循环引用的时候,一个使用retain,一个使用assign;在ARC下,OC对象通常使用strong,非OC对象通常使用assign,而weak就是为了对对象进行弱引用的,如循环引用的时候,一端使用strong,一端使用weak。
好,到这里,本文的目的就基本达到了。不过,还想给大家分享点其他的干货,大部分也是看别人的总结的:
一:strong、weak、unsafe_unretained的区别
执行代码:
self.string1 = @"String 1";
self.string2 = self.string1;
self.string1 = nil;
NSLog(@"String 2 = %@", self.string2);
1:strong:
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, strong) NSString *string2;
输出 String 2 = String 1;因为引用计数加一
2:weak:
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, weak) NSString *string2;
输出 String 2 = null;因为wean的指针会指向同一个地址,引用计数不会加一,但是weak会将指针置为null,防止野指针出现。
3:unsafe_unretained:
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, unsafe_unretained) NSString *string2;
注意这里的程序会crash掉,不会自动将指针置为null,所以这就是它与weak的区别
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
[weakSelf dosomthing];
});
这里没有任何问题;但是下面
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
[weakSelf doSomething];
[weakSelf doOtherSomething];
});
在执行完第一个doSomething之后,weakSelf有可能被释放,这个时候就需要用到__strong了;
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
__strong __typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongelf doOtherSomething];
});
同上看到的,用__weak可以解决循环引用的问题。但是,如果block里要执行多个函数,最好就要加上 __strong;同样的这种巧妙的用法对变量也非常合适,使用过AFNetworking的人大概就见到过这样的对变量的修饰。这样即时block外部释放了该变量,在block的生命周期里,该变量还是可以使用的。2)、问题来了,__block是干嘛的??__block就与__weak正好相反了,它可以改变变量的作用域,使得变量的作用域扩展到block里面。这个时候,就可以对block外部的变量进行操作了(__strong也有这个作用)。那么__block是否可以避免循环引用呢??答案是肯定的,上代码:
__block __typeof__(self) blockSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
[blockSelf doSomething];
[blockSelf doOtherSomething];
blockSelf = nil;
});
如上所示:把blockSelf置为nil是关键。
三、顺带说一下深拷贝和浅拷贝:copy就是浅拷贝,它只拷贝指针,mutableCopy是深拷贝,它会拷贝对象。就是不分父类子类。浅拷贝就是只拷贝父类和子类的指针地址,深拷贝就是父类和子类的对象也都会拷贝。所以,你会发现归档,用的就是深拷贝。经常面试的人会问你,如何让自定义的类具备拷贝功能,则只需要实现NSCoping协议就可以了。
好了。有点乱,希望后续可以多完善一下。也希望各位大牛指正