『IOS』objective-C: nonatomic retain copy assgin 等属性详解

来源:互联网 发布:js settimeout原理 编辑:程序博客网 时间:2024/06/02 04:24

原文地址:http://my.oschina.net/u/728866/blog/90798


对其中对线程安全部分进行详细补充,更便于理解.

/////////////////////////////////////////////////////////////////////////////////////////////////////////

property,可以提供的功能有:提供成员变量的访问方法的声明、控制成员变量的访问权限、控制多线程时成员变量的访问环境 )。

property不但可以在interface,在协议protocol .和类别category中也可以使用.

synthesize的理解是:实现property所声明的方法的定义。

其实说直白就像是:property声明了一些成员变量的访问方法 ,synthesize则定义了由property声明的方法。他们之前的对应关系是

  property 声明方法 ----------》 头文件(.h)中申明的方法

 synthesize定义方法---------》Cpp文件(.m)中定义的方法

不过这里还有有一点细微的差别,后面我会讲到。


先讲property

大家都知道:@property(attribute1 , attribute2, ...])是@property的他的官方表达方式,所以看到attribute1, attribute2,你就应该懂的, 他的用法不是很简单。下面就对他的属性列表进行分类介绍:

下面对属性列表进行一下简单的介绍,后续会用代码来解释。

1.可读性:readonly 、readwrite

@property(readwrite,....) valueType value;

这个属性是变量的默认属性,就是如果你(readwrite and readonly都没有使用,那么你的变量就是readwrite属性),通过加入readwrite属性你的变量就会有get方法,和set方法。

property(readonly,...) valueType value;

这个属性变量就是表明变量只有可读方法,也就是说,你只能使用它的get方法。

2,assign,setter方法直接赋值,不进行任何retain操作,为了解决原类型与环循引用问题对基础数据类型 ,标量赋值。

3,retain,setter方法对参数进行release旧值再retain新值,所有实现都是这个顺序

4,copy,setter方法进行Copy操作,与retain处理流程一样,先旧值release,再Copy出新的对象,retainCount为1。这是为了减少对上下文的依赖而引入的机制。

5  相对原文进行了修改,更便于理解.

a ) atomic的意思就是setter/getter这个函数是一个原语操作。当设置了多线程操作时,如果有两个以上线程同时调用setter的话,不会出现某一个线程执行setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁,以保证多个线程取到的东西的一致性.
b ) nonatomic不保证setter/getter的原语行,所以多线程调用时可能取到东西并不一致,比如setter函数里面改变两个成员变量,如果你用nonatomic的话,getter可能会取到只更改了其中一个变量时候的状态,这样取到的东西会有问题。

如果不需要多线程支持的话,当然nonatomic就够用了,另外由于不涉及锁操作,所以它执行相对快点.

注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。锁被加到所属对象实例级.

所以 不加nonatomic对与多线程是安全的 。


其实他们都可以用代码表示:

1.nonatomic 和 atomic

@property(nonatomic ) NSObject* test1;

@synthesize test1;

上面两句代码,表示我们对test1的访问,是非多线程安全的。


@property(atomic) NSObject* test1;

@synthesize test1;

上面两句代码,表示我们对test1的访问,是多线程安全的。其实也就是在讲该成员变量放到互斥代码中,例如,下面进行加锁。

[_internal lock]; // lock using an object-level lock 
id result = [[value retain] autorelease]; 
[_internal unlock]; 
return result;

就如上面所说 ,这两个属性,是出于对多线程条件下 ,对test1的访问安全。如果你的程序的成员变量不存在安全问题,用nonatomic 就好,因为这样不要在访问是进行互斥,效率更高。


2. readonly 、readwrite (注,后续过程我们都会加入nonatomic 属性,因为它是十分普遍的

2.1 readonly 

@property(nonatomic  ,readonly) NSObject* test1;

@synthesize test1;

上面的两句代码,objc编辑器将会为我们翻译为:

@property(nonatomic  ,readonly) NSObject* test1; 等同

-(NSObject*)test1;

@synthesize test1;等同

-(NSObject*)test1

{

 return test1;

}

2.2 readwrite 

@property(nonatomic  ,readwrite ) NSObject* test1;

@synthesize test1;

上面的两句代码,objc编辑器将会为我们翻译为:


@property(nonatomic  ,readwrite ) NSObject* test1; 等同

-(NSObject*)test1;

-(void)settest1(NSObject* other);

@synthesize test1;等同

-(NSObject*)test1

{

 return test1;

}

-(void)settest1(NSObject* other);

{

 test1 = other;

}

这里要说明一下, readonly 、readwrite 这两个属性他们的真正价值,不是提供成员变量访问接口,而是控制

成员变量的访问权限。所以要抓住他们真正价值。

3. assign

@property(nonatomic  ,assign) NSObject* test1;

@synthesize test1;

上面两句:objc编辑器将会翻译如下:

@property(nonatomic  ,assign) NSObject* test1;等同

-(void)settest1(NSObject* other);

@synthesize test1;

-(void)settest1(NSObject* other);等同

{

 test1 = other;

}


4. retain

@property(nonatomic  ,retain) NSObject* test1;

@synthesize test1;

objc编辑器翻译如:

@property(nonatomic  ,retain) NSObject* test1;等同

-(NSObject*)test1;

-(void)settest1(NSObject* other);

@synthesize test1; 

-(NSObject*)test1

{

 return test1;

}

-(void)settest1(NSObject* other)

 {  

  if (test1!= other)

    {

                   [test1release];

                   test1= [otherretain];

     }

}


5. copy

@property(nonatomic  ,copy) NSObject* test1;

@synthesize test1;

objc编辑器将翻译如:

@property(nonatomic  ,copy) NSObject* test1;

-(NSObject*)test1;

-(void)settest1(NSObject* other);

@synthesize test1;

-(NSObject*)test1

{

 return test1;

}

-(void)settest1(NSObject* other);

{

    if (test1!=other) { 
        [test1release]; 
        test1= [othercopy]; 
    } 

}

对于Copy属性有一点要主要,被定义有copy属性的对象必须要符合NSCopying协议,并且你还必须实现了

-(id)copyWithZone:(NSZone*)zone该方法



代码才是王道:都是一些简单代码用例

//为了更具有普遍性,我选择用自定义对象testObj 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//testObj .h

@interface testObj : NSObject<NSCopying>{
    
}
@end 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//testObj .m

-(id)copyWithZone:(NSZone *)zone
{
    testObj* pObj = [[testObj allocWithZone:zone] init];
    return pObj;
}
@end

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//testApp是包含多个testObj 对象指针

//testApp .h
@interface testApp :
{
    testObj*  test1;
    testObj*  _test2;
}

@property(nonatomic , retain) NSObject* test1;
@property(nonatomic , copy) NSObject* test2;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//testApp.m

@synthesize test1;

@synthesize test2 = _test2;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@synthesize test2 = _test2; //对这里要特别主要一下,这里可以看作是一种别名机制,这点和前面类比的C++头文件和Cpp文件不同。

例如对于setter函数,objc编辑器将会按如下方式翻译

如果是@synthesize _test2;

setter函数将是这种形式:

-(void)set_test2(NSObject*); //注意中间的下划线

写成@synthesize test = _test2;

setter函数将是这种形式:
 -(void)settest2(NSObject*);// _test2 被 test2替换了

可以看出这种别名机制,感觉是规范书写(其实更像是规范Objc的书写,大家可以看看官方文档中,成员变量都是前面带下滑线的(如_test2),所以才搞了这样一个别名规范代码中的书写)


//下面是一个功能函数,

-(void )test

{

test1 = [[testObj alloc]init];// test1  retainCount  =1;

//下面有三种对test2操作方法:

  _test2 = test1//这里是将test1的指针赋值给_test2指针,注意,并没有调用test2的setter方法 ,所以test retainCount  = 1、 test2 retainCount  = 0;

  self.test2 = test1; //这里调用test2的Copy方法,因此这是test retainCount  = 1、 test2 retainCount  = 1; 

  test2   = test1;

 //这段代码系统将会提示出错说test2没有定义。因为这里编译器认为是一条赋值表达式,将test2看作是一个成员变量,而在我们的testApp 中是没有这个成员变量的,这里要区别我们所说的别名,别名机制可以看作是在调用setter或者getter函数才会起作用,而这里只是一个简单的赋值, 也就出现未定义的错误。如果没有理解,你就记住要调用setter或者getter函数,就用"self.成员变量"这种形式就行了.


//我们把  test2   = test1这行代码注释掉,保证程序继续执行

    [test1 release];  // 释放test1 ,test1 retainCount  = 0;
    
    [test2 release]; // 释放test2 ,test2 retainCount  = 0;

}



/////////////////////////////////////////////

weak and strong property (强引用和弱引用的区别)

 weak 和 strong 属性只有在你打开ARC时才会被要求使用,这时你是不能使用retain release autorelease 操作的,因为ARC会自动为你做好这些操作,但是你需要在对象属性上使用weak 和strong,其中strong就相当于retain属性,而weak相当于assign。

只有一种情况你需要使用weak(默认是strong),就是为了避免retain cycles(就是父类中含有子类{父类retain了子类},子类中又调用了父类{子类又retain了父类},这样都无法release)

ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在 iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 以后可以使用该特性。



property 中的strong 与weak


strong关键字与retain关似,用了它,引用计数自动+1,用实例更能说明一切

@property (nonatomic, strong) NSString *string1; 

@property (nonatomic, strong) NSString *string2; 


有这样两个属性,


@synthesize string1; 

@synthesize string2; 




猜一下下面代码将输出什么结果?


self.string1 = @"String 1"; 

self.string2 = self.string1; 

self.string1 = nil; 

NSLog(@"String 2 = %@", self.string2); 



结果是:String 2 = String 1

由于string2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"String 1", 如果你对retain熟悉的话,这理解并不难。


接着我们来看weak关键字:

如果这样声明两个属性:


@property (nonatomic, strong) NSString *string1; 

@property (nonatomic, weak) NSString *string2; 


并定义 

@synthesize string1; 

@synthesize string2; 


再来猜一下,下面输出是什么?


self.string1 = @"String 1"; 

self.string2 = self.string1; 

self.string1 = nil; 

NSLog(@"String 2 = %@", self.string2); 


结果是:String 2 = null


分析一下,由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1=nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。在c/c++开发过程中,为何大牛都说指针的空间释放了后,都要将指针赋为NULL. 在这儿用weak关键字帮我们做了这一步。

以上来自 http://www.cnblogs.com/mybkn/archive/2012/03/08/2384860.html


关于一些UI的property应该使用retain、strong还是weak还有其他一些问题

关于一些UI的property应该使用retain、strong还是weak。
1、在斯坦福大学的那个视频上看到的是说使用weak,ios5会帮你做剩余的一切,连release也不用了,dealloc都不用重载了(或许我没看那么认真,感觉好像是这样)。
2、在公司的项目代码上经常看到使用retain
3、使用@property进行了声明,还需要声明实例变量吗(在Interface下面打两个花括号那种)。在斯坦福大学的那个视频上好像没看到使用实例变量(目前看到第三个)。
4、目前流行的iphone4和iphone4s是使用ios4吗?那为了兼容,是否不应该使用ios5的新特性。


1、对于retain来说,一般指的是指针,这些属性需要保存引用计数,防止出现僵尸的情况,当时对于NSString类型,这个不是retain,而是copy,但是对于strong来说,这些你都不需要考虑,它会自己判断是选择retain还是copy,而对于assign来说,一是非指针变量,比如说NSInteger之类的,还有就是避免出现循环引用的时候,对于weak,其和assign差不多,但是它多了一点,就是,它会自动对该类型变量设置为nil。
2、至于公司经常使用retain的原因,一个是由于编码习惯,还有就是根据项目需要。
3、是否需要声明实例变量,这些都要看特殊情况的,假如在interface中你并没有声明实例变量,但是你在对于的.m文件中@synthesize的时候,那个时候其实就表明了你已经声明这个实例变量了。
4、对于这个问题,系统是可以升级的,并没有什么规定iphone4s只能使用IOS4。



1.具体一点:IBOutlet可以为weak,NSString为copy,Delegate一般为weak,其他的看情况。一般来说,类“内部”的属性设置为strong,类“外部”的属性设置为weak。说到底就是一个归属权的问题。小心出现循环引用导致内存无法释放。
2.不用ARC的话就会看到很多retian。
3.如果你写了@synthesize abc = _abc;的话,系统自动帮你声明了一个_abc的实例变量。


  使用assign: 对基础数据类型 (NSInteger)和C数据类型(int, float, double, char,等)
  使用copy: 对NSString 
  使用retain: 对其他NSObject和其子类


0 0
原创粉丝点击