关于Objective-C中的@property以及属性分析

移动开发 waitig 454℃ 百度已收录 0评论

前言:关于@property可能你每天都在使用,然而@property真正意义以及里面的属性内容是很多人容易忽略的地方。重要的事情说三遍,这是重点重点重点。

一:@property到底是什么?

@Property是声明属性的语法,它可以快速方便的为实例变量创建存取器,并允许我们通过点语法使用存取器。
存取器(accessor):指用于获取和设置实例变量的方法。用于获取实例变量值的存取器是getter,用于设置实例变量值的存取器是setter。

二:怎样创建存取器?

通常有两种方法来创建存取器,一种是手动创建一种是 使用@Property创建存取器。

1:通过手动创建存取器。
我们在XX类中的中声明两个实例变量Str1和Str2,并且可以看到分别对这两个实例变量声明了get/set方法,即存取器。代码如下:

@interface XX : NSObject
{
    // 实例变量
    NSString *str1;
    NSString *str2;
}
// setter                                   
- (void)setStr1:(NSString *)newStr1; 
// getter
- (NSString *)str1;
// setter
- (void)setStr2:(NSString *)newStr2;
// getter
- (NSString *)str2;
@end</foundation>

下面我们来对存取器即setter和getter方法进行实现。其实存取器就是对实例变量进行赋值和取值。按照规定好的赋值方法以set开头,取值方法以实例变量名命名。代码如下:

@implementation Car
// setter
- (void)setStr1:(NSString *)newStr1; 
{
    setStr1 = newStr1;
}
// getter
- (NSString *)str1;
{
    return str1;
}
// setter
- (void)setStr2:(NSString *)newStr2; 
{
    setStr2 = newStr2;
}
// getter
- (NSString *)str2;
{
    return str2;
}
@end

使用存取器

        XX *x = [[Car alloc] init];
 存:
        [x setStr1:@"123"];
        [x setStr2:@"456"];
 取:
        [x str1];
        [x str2];

2:通过@property创建存取器
先在.h文件声明使用@property声明两个属性

@interface XX : NSObject
{
    // 实例变量
    NSString *str1;
    NSString *str2;
}
@property(nonatomic,strong) NSString *str1;
@property(nonatomic,strong) NSString *str2;
@end

然后在.m文件使用synthesize自动生成这两个实例变量的存取器,并且隐藏了存取器,虽然我们看不到存取器,但它们确实是存在的。
@implementation XX
@synthesize str1;
@synthesize str2;
@end

最后使用存取器

        XX *x = [[Car alloc] init];
 存:
        [x setStr1:@"123"];或者 x.setStr1=@"123";
        [x setStr2:@"456"];或者 x.setStr2=@"123456;
 取:
        [x str1];或者x.str1;
        [x str2];或者x.str2;

总结:
使用@Property,就不必单独声明实例变量了。因为在没有显示提供示例变量声明的前提下,系统会自动帮你生成实例变量。
@property等同于在.h文件中声明实例变量的get/set方法,@synthesize等同于在.m文件中实现实例变量的get/set方法。使用@property和synthesize创建存取器要比手动声明两个存取方法(getter和setter)更简单。而且我们在使用属性时可以使用点语法赋值或取值,语法更简单,更符合面向对象编程。

注意:如果我们希望使用默认的实例变量命名方式,那么我们在.m文件中就不需要使用@synthesize声明,系统会帮我们自动完成。如果我们希望自己命名实例变量命,那么我们就使用@synthesize显示声明我们希望的实例变量名。
如:

@implementation XX
@synthesize str1 = NewStr1;
@synthesize Str2 = NewStr2;

使用的时候使用NewStr1和NewStr2就好了。

三:关于@property里的一些特性的分析和总结。

1:常见的属性有哪些都有哪些意义。如下:
– readwrite 产生setter\getter⽅方法
- readonly 只产生简单的getter,没有setter, 默认的读写属性
– setter 指定生成setter⽅法的名字
– getter 指定生成getter方法的名字
– assign 默认类型,setter方法直接赋值,而不进⾏retain操作,适⽤于基本数据类型, 对象类型, 不会发⽣生引用计数变化
– retain setter方法对参数进⾏行release旧值,再retain新值
– copy setter方法进⾏行Copy操作,与retain一样
– atomic 保证多线程访问下的安全,但浪费系统资源,原子性控制的默
认设置
– nonatomic 禁⽌多线程,变量保护,提高性能
– nonnull 表示对象可以是NULL或nil
– nullable 表⽰对象不应该为空

2:属性对比分析
1):atomic 和 nonatomic
atomic 是默认的属性,表示对象的操作属于原子操作,主要是在多线程的环境下,提供多线程访问的安全。 我们知道在多线程的下对对象的访问都 需要先上锁访问后再解锁,保证不会同时有⼏个操作针对同⼀个对象。 如果编程中不涉及到多线程,不建议使用,因为使用atomic⽐比nonatomic更耗费系统资源。
nonatomic 表⽰访问器的访问不是原⼦操作,不支持多线程访问安全,但 是访问性能⾼。

2)readwrite 和 readonly
readwrite 是默认的属性,表⽰可以对象进⾏读和写,自动生成setter和getter⽅法。
readonly 表示只允许读取对象的值,只会生成对象的getter⽅法。
//以下等价
@property (nonatomic,retain) NSObject *object;
@property (nonatomic,retain,readwrite) NSObject *object;

3)< retain >, < assign > 和< copy >
retain 表⽰示对NSObject和及其⼦子类对象release旧值,再retain新值,使对象的应⽤计数增加⼀。
该属性只能使⽤用于obejective-c类型对象,不能用于Core Foundation对象。
assign 是默认属性,只可以对基本数据类型(如CGFloat, NSInteger,Bool,int,代理对象)等使⽤。该方式会对象直接赋值而不会进行retain操作。
copy 表⽰重新建立一个新的计数为1的对象,然后释放掉旧的值。

都知道retain是对指针的拷⻉,copy是对内容的拷⻉。
比如:NSString 对象 的地址为0x100,其内容为“string”如果使用copy到另外一个NSString对 象,则会⽣生成另外⼀个地址为0x110的对象,只不过内容仍然是’string“。如果使用retain到另外一个NSString对象,则该对象的地址仍然为0x100,只不过 该对象的计数变为2

retain 是指针拷贝
copy   是内容拷贝
//在拷贝之前,都会释放旧的对象。
assign:简单赋值,不更改索引计数(Reference Counting)。
copy  :建立一个索引计数为1的对象,然后释放旧对象
retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的输入对象的索引计数为1
//事例说明
@property (nonatomic,retain) TestObject *object;
@property (nonatomic,copy  ) TestObject *object;

/********** Getter等效代码***********/
//.m中getter等效代码
- (TestObject*)object{
   return object;
}
//.m中getter等效代码
- (TestObject*)object{
  [object retain];
  return  [object autorelease];//用完立即释放
}

/********** Setter等效代码分析***********/
- (void)setObject:(TestObject*)newObject{
  if(object != newObject){
    [object release];
    object = [newObject copy];
  }
}

4) strong 和 weak
目的:对象声明时需要加⼊入strong和weak,方便内存的自动管理。
->strong:强引⽤,默认的属性,类似于retain,其实是一个相对的概念,就是一个引用。
默认的所有实例变量和局部变量都是strong指针。
如果有一个强引⽤持有该对象,则该对象就不能被释放。
->weak:弱引⽤,类似于assign,弱引用除了不决定对象的存亡外,其他与强引用相同。
即使一个对象被持有⽆数个若引用,只要没有强引用指向他,那麽其还是会被清除,它不是对象的拥有者。
其值会在对象被释放后自动设置为nil。
weak指针主要用于“父-子”关系,父亲拥有⼀个⼉子的strong指针
因此父亲是儿子的所有者;但为了阻止所有权循环,儿⼦需要使用weak指针指向父亲。

典型例⼦是delegate模式
ViewController通过strong指针(self.view)拥有⼀个UITableView的dataSource和delegate都是weak指针

5) nonnull 和 nullable
1. swift:可以使⽤!和?来表⽰一个对象是optional的还是non- optional,如view?和view!。
2. Objective-C:没有这一区分,view即可表示这个对象是optional,也可表示是non-optional。
说明:这样就会造成一个问题: 在Swift与Objective-C混编时,Swift编译器并不知道一个Objective-C对象到 底是optional还是non-optional,因此这种情况下编译器会隐式地将Objective- C的对象当成是non-optional。  为了解决这个问题,苹果在Xcode 6.3引⼊入了⼀个Objective-C的新特性: nullability annotations。这⼀新特性的核⼼心是两个新的类型注释:nullable 和nonnull。

->nullable 表⽰对象可以是NULL或nil
->nonnull 表示对象不应该为空
说明:当我们不遵循这一规则时,编译器就会给出警告。
在任何可以使用const关键字的地⽅都可以使⽤nullable和nonnull,不过这两个关键字仅限于使用在指针类型上。
在⽅法的声明中,我们还可以使用不带下划线的nullable和nonnull

//建议对象属性不能为空,使用方式1

@property (nonatomic,copy,nonnull) NSArray *items;//方式1
@property (nonatomic,copy        ) NSArray *items;//方式2

**重点说明:手动实现getter和setter方法(atomic/nonatomic/retain/ assign/copy定义的对象)
只是给编译器的建议,编译器首先会到代码⾥里⾯去找。
1.如果定义了相应的getter和setter的方法,那么好,⽤手动定义的getter&&setter方法
2.如果没有,编译器就会根据对象自动生成相应的getter和setter方法**

重要总结总结总结

1.copy  使用类型 :NSString,block
2.assign使用类型 :delegate,int,float,NSInteger,bool,枚举,结构体...
3.retain使用类型 :NSArray,NSDate
4.strong使用类型 :NSString/block以外的OC对象
5.weak  使用类型 :当2个对象相互引用,一端用strong,一端用weak6.readOnly     :只读时候(即只需要getter方法的时候);
  readWriete   :默认属性(getter&&setter方法)

属性中的修饰词分析 :

assign ( ARC/MRC )
1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰 .
2.如果没有使用 weak strong retain copy 修饰 , 那么默认就是使用 assign 了. ( 它们之间是有你没我的关系 )
3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 )
4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 . ( 与weak的区别 )


weak ( ARC )
1.弱指针是针对对象的修饰词 , 就是说它不能修饰基本数据类型 .
2.weak 修饰的对象计数器不会+1 , 也就是直接赋值 .
3.弱引用是为打破循环引用而生的 .
4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向 nil . 而 nil 访问什么鬼都不会报野指针错误 .


strong ( ARC )
1.直接赋值并且计数器 +1 .
2.在 ARC 里替代了 retain 的作用 .


retain ( MRC )
1.release 旧对象( 旧对象计数器 -1 ) , retain 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
2.在set方法里面是这样的 :

if (_delegate) {
[_delegate release];
}
_delegate = [delegate retain];


copy ( ARC/MRC )
1.copy 在 MRC 时是这样做的 release 旧对象( 旧对象计数器 -1 ) , copy 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
1.1在set方法里面是这样的 :

if (_delegate) {
[_delegate release];
}
_delegate = [delegate copy];
2.copy 在 ARC 时是这么干的 copy 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
2.1在set方法里面是这样的 :

_delegate = [delegate copy];
3.使用注意 :
3.1 修饰的属性本身要不可变的 . 例如 NSMutableArray 采用 copy 修饰 , 添加元素表面上可以 一到运行就崩溃了 , 因为 copy 过后实际上成了NSArray了 . ( 队友 , 我们不吭你 )
3.2 遵守 NSCopying 协议的对象使用 .


nonatomic ( ARC/MRC )
1.不对set方法加锁 .
2.性能好
3.线程不安全

atomic ( ARC/MRC )
1.原子属性就是对生成的 set 方法加互斥锁 @synchronized(锁对象) .
@synchronized(self) {
_delegate = delegate;
}
2.需要消耗系统资源 .
3.互斥锁是利用线程同步实现的 , 意在保证同一时间只有一个线程调用 set 方法 .
4.其实还有 get 方法 , 要是同时 set 和 get 一起调用还是会有问题的 . 所以即使用了 atomic 修饰 还是不够安全 .


readonly
1.让 Xcode 只生成get方法 .
2.不想把暴露的属性被人随便替换时 , 可以使用 .


readwrite
1.让 Xcode 生成get/set方法 .
2.不用 readonly 修饰时 , 默认就是 readwrite .


getter/setter 的自定义方法名 .
1.一般对于 有/无 是/否 等这样的属性 , getter 方法名前面加个 is 会显得通俗易懂 .

如有错误欢迎提出,本文中有部分借鉴别的文章这里写链接内容。


本文由【waitig】发表在等英博客
本文固定链接:关于Objective-C中的@property以及属性分析
欢迎关注本站官方公众号,每日都有干货分享!
等英博客官方公众号
点赞 (0)分享 (0)