iOS学习笔 - Objective-C property和instance variable

iOS学习笔 - Objective-C property和instance variable


一、property语法
声明property的语法为:
@property (参数)类型名字;

这里的参数主要分为三类:
1、读写属性(readwrite/readonly,他们是互斥的)

readwrite
此标记说明属性会被当成读写的,这也是默认属性。设置器和读取器都需要在@implementation中实现。如果使用@synthesize关键字,读取器和设置器都会被解析。

readonly
此标记说明属性是只读的,默认的标记是读写,如果你指定了只读,在@implementation中只需要一个读取器。或者如果你使用@synthesize关键字,也是有读取器方法被解析。而且如果你试图使用点操作符为属性赋值,你将得到一个编译错误。

2、setter语意(strong/weak /copy/assign/retain,他们是互斥的)
与所有权有关系的属性,关键字间的对应关系。
属性值          关键字            所有权
strong             __strong          有
weak              __weak           无
unsafe_unretained   __unsafe_unretained 无
copy              __strong          有
assign             __unsafe_unretained无
retain             __strong          有

 

strong
Specifies that there is a strong (owning) relationship to the destination object.
该属性所声明的变量将成为对象的持有者。


weak
Specifies that there is a weak (non-owning) relationship to the destination object.If the destination object is deallocated, the property value is automatically set to nil.
(Weak properties are not supported on OS X v10.6 and iOS 4; useassign instead.)
该属性所声明的变量将没有对象的所有权,并且当对象被deallocated之后,对象将被自动赋值nil。
delegate和 Outlet应该用 weak属性来声明。同时, iOS 5之前的版本是没有 __weak关键字的,所以 weak属性是不能使用的。这种情况我们使用 unsafe_unretained。
 
unsafe_unretained
等效于__unsafe_unretaind关键字声明的变量;像上面说明的,iOS 5之前的系统用该属性代替 weak来使用。
 
copy
Specifies that a copy of the object should be used for assignment.
The previous value is sent a release message.
The copy is made by invoking the copy method. This attribute is valid only for object types, which must implement the NSCopying protocol.

此标记说明在赋值时使用传入值的一份拷贝。拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。
与 strong的区别是声明变量是拷贝对象的持有者。


assign
Specifies that the setter uses simple assignment. This attribute is the default.
You use this attribute for scalar types such as NSInteger and CGRect.

此标记说明设置器直接进行赋值,这也是默认值。一般Scalar Varible用该属性声明,比如,int, BOOL。


retain
Specifies that retain should be invoked on the object upon assignment.
The previous value is sent a release message.
In OS X v10.6 and later, you can use the __attribute__ keyword to specify that a Core Foundation
property should be treated like an Objective-C object for memory management:
@property(retain) __attribute__((NSObject)) CFDictionaryRef myDictionary;

指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(因为retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数)。
该属性与 strong一致;只是可读性更强一些。


3、原子性atomicity(nonatomic)
nonatomic
指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic,那么访问器只是简单地返回这个值。但是线程安全访问会消耗一定的资源。所以如果不是多线程的程序,打上(nonatomic)。
 
二、property之self
1、常用方式:
1)文件名称:Person.h

#import 

@interface Person : NSObject{  

    NSString *firstName; //属性本身

    NSString *fullName;//属性本身

}

@property(nonatomic, retain) NSString *firstName;//存取方法名

@property(nonatomic, retain) NSString *fullName;//存取方法名

@end


2)文件名称: Person.m

#import"Person.h"

@implementation Person

@synthesize firstName;

@synthesizefullName

-(void)dealloc{  

    [firstName release];

    [fullName release];  

    [super dealloc];

}

@end


/*

 使用@property@synthesize实现Student的成员属性的set get方法

 上面的例子是通常的用法,即属性名和方法名是一样的。

 @property是设置存取方法,和属性无关,完全可以这么写

 @property (readonly) NSString fullName;

 在类里面实现

 -(NSString)getFullName{

 retrun @"hello";

 }

 同样可以使用self.fullName调用。

 */


所以看下下面的第2中方式

2、第2种方式:

1)文件名称:PersonEx.h 

#import

@interface PersonEx : NSObject{  

    NSString *_firstName; //属性本身

    NSString *_fullName;//属性本身

@property(nonatomic, retain) NSString *firstName;//存取方法名

@property(nonatomic, retain) NSString *fullName;//存取方法名

@end


2)文件名称:PersonEx.m

#import"PersonEx.h"

@implementation PersonEx

@synthesize firstName=_firstName;

@synthesizefullName=_fullName;

-(void)dealloc{   

    [_firstName release];

    _firstName =nil;  

    [_fullName release];

   _fullName =nil;   

    [super dealloc];

}


/*

 getter方法:

 -(NSString*)getFullName{

 return _fullName;

 }

 

 setter方法:

 // assign

 -(void)setFullName:(NSString *)fullName{

 _fullName = fullName;

 }

 

 // retain

 -(void)setFullName:(NSString *)fullName{

 if (_fullName != fullName) { 

 [_fullName release];

 _fullName = [fullName retain]; //注意

 }    

 }

 

 // copy

 -(void)setFullName:(NSString *)fullName{

 if (_fullName != fullName) {

 [_fullName release];

 _fullName = [fullName copy]; //注意

 }  

 }

 */  

@end


3、调用的类

1)文件名称:ViewController.h

#import

@class PersonEx;

@interface ViewController : UIViewController{

    PersonEx *_person;

}

@property (nonatomic, retain) PersonEx *person;

@end


2)文件名称:ViewController.m

#import"ViewController.h"

#import"Person.h"

#import"PersonEx.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesizeperson;

- (void)viewDidLoad

{

    [super viewDidLoad];   

    

   //1、加self的方式:

    PersonEx *person1 = [[PersonEx alloc] init]; //person1对象retainCount = 1;

    NSLog(@"person1 retainCoun=%d", [person1 retainCount]);

   self.person = person1; //person对象retainCount = 2;

    NSLog(@"self.person retainCoun=%d", [self.person retainCount]);

    [person1 release];     //person1对象retainCount = 1;

   // retainCount指对象引用计数,PersonExpropertyretain默认使用self.person1引用计数+1

    NSLog(@"person1 retainCoun=%d", [person1 retainCount]);

    

   //2、不加self的方式

    PersonEx *person2 = [[PersonEx alloc] init]; //person2对象retainCount = 1;

    NSLog(@"person2 retainCoun=%d", [person2 retainCount]);

    person = person2;  //person1对象retainCount = 1;

    NSLog(@"person retainCoun=%d", [person retainCount]);

    [person2 release]; //person2对象内存已释放  

    

   //3、加self直接赋值方式

   self.person = [[PersonEx alloc] init]; //PersonEx对象retainCount = 2;容易造成内存泄露

    //由于objective-c内存管理是根据引用计数处理的,当一个对象的引用计数为零时,才会释放该内存。

    NSLog(@"self.person retainCoun=%d", [self.person retainCount]);

   //    self._person = [[PersonEx alloc] init]; //Property '_person' not found on object of type....

}


-(void)dealloc{   

    [_person release];

   _person =nil;

    [super dealloc];

}


@end


三、重点分析@synthesize name=_name;

先看下下面的代码,经常在别人的源码中看到:

@interface Person : NSObject

@property(nonamtic, retain) NSString *name;

@end

@implementatin Person

@synthesize name=_name;    

@end

一直对@synthesize name=_name;这样的写法觉的很奇怪,这个_name是哪里来的?明明找不到它的定义,为什么编译器没报错?原来这个是系统生成的:

64位系统中,系统会自动给类添加ivarinstance variable),添加的ivar以一个下划线“_”做前缀,即类似_ivar这样的格式。

但在32位系统中,如果类的@interface部分没有进行ivar声明,但有@property声明,在类的@implementation部分有相应的@synthesize,则会得到类似下面的编译错误:

Synthesize property ‘xxx’ must either be named the same as a compatible ivar or must explicitly name an ivar。(可惜找不到32位系统验证这个问题)

所以最好写成像下面这样:

@interface Person : NSObject{

    NSString *_name;

}

@property(nonamtic, retain) NSString *name;

@end

@implementatin Person

@synthesize name=_name;

-(void)dealloc{

    [_name release];

    [super dealloc];

}    

@end

1、这样做的原因之一就是不暴露实例的成员变量。

在这里简单说一下_namename的区别。_namePerson类的成员变量,name是属性。

属性是用self.name,通过getter方法来调用的,可以在类外使用。

而成员变量是通过_name来调用,只能在该类对应的implementation中使用,在类外不能使用。

在类内方法访问成员变量时就应该直接使用成员变量,即带下划线的名字_name,类内方法不推荐使用self.name,因为使用存取器本来就是对外的,在内部使用可能会造成一些不必要的错误,比如:

self.name= [[NSString alloc] init];     //retain count为2,处理不好易导致内存泄露。

2、这样的写法还能避免一些奇怪的问题

propertysynthesize定义了一对gettersetter方法,在这里的getter方法是namesetter方法是setName,事实上gettersetter方法操作的是变量_name

如果是@synthesize name=_name;getter方法为:

-(MyObject *)name

{

   return _name;

}    

如果是@synthesize name;getter方法为: 

-(MyObject *)name

{

   return name;

}    

当函数名和属性名重名的时候会出现未知的错误,为了避免这种情况,Apple的源码范例里面多数也采用@synthesize name=_name;这种方式。

 

在《Learn Objective-C on the Mac》中有这么一段话:

编译器和苹果公司都以下划线开头的形式保存实例变量名称,如果你尝试在其他地方使用下划线,可能出现严重的错误。这条规则不是强制性的,但是如果不遵守它,你可能会遇到某种风险。

你可能感兴趣的:(IOS)