当前位置: 移动技术网 > IT编程>移动开发>IOS > 52个有效方法(6) - 理解“属性”这一概念

52个有效方法(6) - 理解“属性”这一概念

2019年10月15日  | 移动技术网IT编程  | 我要评论

属性

“属性”(property)是oc的一项特性,用于封装对象中的数据。

  • @property是声明属性的语法(@property = ivar + getter + setter)

    oc对象通常会把其所需的数据保存为各种实例变量(ivar)。实例变量一般通过“存取方法”(accessmethod)来访问。

    什么是存取方法:gettersetter 方法(access method = getter + setter),其中getter 用于获取变量value, 而setter 用于写入value

  • @property可以快速方便的为实例变量创建存取器。

//  man.h
#import <foundation/foundation.h>

@interface man : nsobject
@property (nonatomic,strong)nsstring *name;
@property (nonatomic,strong)nsstring *sex;
@end

与下面的写法等效

//  man.h
#import <foundation/foundation.h>

@interface man : nsobject
{
    // 实例变量
    nsstring *name;
    nsstring *sex;
}
// setter
- (void)setname:(nsstring *)newname;

// getter
- (nsstring *)name;

// setter
- (void)setsex:(nsstring *)newsex;

// getter
- (nsstring *)sex;
@end
  • 通常使用“点语法” 来让编译器自动调用相关的存取方法(access method = getter + setter)。

    self. name = @"sky";
    nsstring *name = self. name;

    点语法有什么优势呢?

    1. 省时,省力 :如果使用了属性,编译器会自动编写访问属性所需的方法。这个过程由编译器在编译期执行,看不到这些get set 源代码。

    2. 编译器会自动向类中添加适当类型的实例变量,并且在属性名前添加下划线。

  • 如果你不想让编译器自动合成存取方法,则可以自己实现。如果你只实现了其中一个存取方法,那么另一个还是会由编译器来合成。

  • 当我们同时重写了setter and getter方式时,系统会报错,原因是找不到实例变量。其解决方法: 在.m的文件中使用@synthesize

  • @synthesize是为属性添加一个实例变量名,或者说别名。同时会为该属性生成 setter/getter 方法。

  • protocol中使用property只会生成settergetter方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性。需要使用@synthesize生成settergetter

  • 当你在子类中重载了父类中的属性,你必须 使用@synthesize来手动合成ivar

  • 当我们同时重写了setter and getter方式时,需要在.m的文件中使用@synthesize

    //  man.m
    #import "man.h"
    
    @implementation man
    @synthesize name = _name;
    // setter
    - (void)setname:(nsstring *)name
    {
      _name = name;
    }
    
    // getter
    - (nsstring *)name
    {
      return _name;
    }
    @end
    • **@synthesize name = _name**

      • _name是成员变量

      • name是属性

      • 作用是告诉编译器name属性为_name实例变量生成setter and getter方法的实现

      • name属性的setter方法是setname,它操作的是_name这个变量

      • @synthesize中定义与变量名不同的settergetter的命名,以此来保护变量不会被不恰当的访问setter=<name>这种不常用,也不推荐使用)

          //setter=<name>这种不常用,也不推荐使用
        @property (nonatomic, setter = mysetter,getter = mygetter ) nsstring *name;
        
        @property (nonatomic,getter = ishidden ) bool hidden;
  • @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize@dynamic都没写,那么默认的就是@syntheszie var = _var

  • 如果某属性已经在某处实现了自己的 setter/getter ,可以使用 @dynamic来阻止 @synthesize 自动生成新的 setter/getter 覆盖。

  • @dynamic告诉编译器:属性的 settergetter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。

  • 假如一个属性被声明为 @dynamic var,然后你没有提供@setter方法和 @getter 方法。编译的时候没问题,但是当程序运行到 instance.var = somevar,由于缺 setter 方法会导致程序崩溃。或者当运行到 somevar = var时,由于缺 getter 方法同样会导致崩溃。

  • 编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

//  man.h
#import <foundation/foundation.h>

@interface man : nsobject
@property (nonatomic,strong)nsstring *name;
@end
//  man.m
#import "man.h"

@implementation man
@dynamic name;
// setter
// - (void)setname:(nsstring *)name
// {
//   _name = name;
// }
// getter
- (nsstring *)name
{
  return _name;
}
@end

调用时会出现崩溃

    man *man = [[man alloc] init];
    man.name = @"sky";//缺 setter 方法会导致程序崩溃
    nsstring *name = man.name;//缺 getter 方法同样会导致崩溃

属性特质

原子性

  • atomic(默认):atomic意为操作是原子的,意味着只有一个线程访问实例变量(生成的settergetter方法是一个原子操作)。atomic是线程安全的,至少在当前的存取器上是安全的。它是一个默认的特性,但是很少使用,因为比较影响效率。

  • nonatomicnonatomic意为操作是非原子的,可以被多个线程访问。它的效率比atomic快。但不能保证在多线程环境下的安全性,开发中常用。

  • 开发ios程序时应该使用nonatomic属性,因为atomic(同步锁)属性严重影响性能。该属性使用了同步锁,会在创建时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,通过声明nonatomic可以节省这些虽然很小但是不必要额外开销。

存取器控制

  • readwrite(默认):readwrite是默认值,表示该属性同时拥有settergetter

  • readonlyreadonly表示只有getter没有setter

  • 有时候为了语意更明确可能需要自定义访问器的名字。

//setter=<name>这种不常用,也不推荐使用
@property (nonatomic, setter = mysetter,getter = mygetter ) nsstring *name;
  

@property (nonatomic,getter = ishidden ) bool hidden;

  • assign(默认):assign用于非指针变量(值)类型,统一由系统栈进行内存管理。一般用于基础类型和c数据类型,如intfloatdoublensintegercgfloat等表示单纯的复制。还包括不存在所有权关系的对象,比如常见的delegate

  • retain:在setter方法中,需要对传入的对象进行引用计数加1的操作。

  • strongstrong是在ios引入arc的时候引入的关键字,是retain的一个可选的替代。对传入的对象的强引用,会增加对象的引用计数。strongretain的意思相同并产生相同的代码,但是语意上更好更能体现对象的关系。

  • weak:对传入的对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil

  • copy:与strong类似,但区别在于copy是创建一个新对象,strong是创建一个指针,引用对象计数加1

举例说明weakstrongcopy属性特质的差异

  • 首先创建两个自定义的person类的实例变量,并分别用weakstrong修饰。
@property (nonatomic,strong) person *strongperson;
@property (nonatomic,weak) person *weakperson;
  • strongperson属性置nil
self.strongperson = [[person alloc] init];
self.weakperson = self.strongperson;
self.strongperson = nil; 
nslog(@"strongstr=%@,weakstr=%@",self.strongperson,self.weakperson);

输出结果为:strongstr=(null),weakstr=(null)。说明weak修饰的属性并不会使引用计数增加。

  • 如果使用nsstring类进行上述类似操作,得到的结果是不同的。
@property (nonatomic,strong) nsstring *strongstr;
@property (nonatomic,weak) nsstring *weakstr;
···
self.strongstr = @"string";
self.weakstr = self.strongstr;
self.strongstr = nil;
nslog(@"strongstr=%@,weakstr=%@",self.strongstr,self.weakstr);

输出结果为:strongstr=(null),weakstr=string。这里主要是因为nsstring类型的赋值默认会加上copy,而copy会创建一个新的对象。这里的赋值语句其实是

self.strongstr = [@"string" copy];
self.weakstr = [self.strongstr copy];
  • weakperson属性置nil
self.strongperson = [[person alloc] init];
self.weakperson = self.strongperson;
self.weakperson = nil;
nslog(@"strongstr=%@,weakstr=%@",self.strongperson,self.weakperson);

输出结果如下:strongstr=<person: 0x600000007d50>,weakstr=(null)。说明weak修饰的属性只是对对象的弱引用,并不会真正的持有该对象。

  • 新建一个person类实例变量p,赋值strongperson后将pnil
person *p = [[person alloc] init];
self.strongperson = p;
self.weakperson = self.strongperson;
p = nil; 
nslog(@"strongstr=%@,weakstr=%@",self.strongperson,self.weakperson);

输出结果为:strongstr=<person: 0x600000200b50>,weakstr=<person: 0x600000200b50>。因为strong属性会强引用该对象并使该对象的引用计数+1,所以即使把p设置为nil,该对象也并没有释放,要想释放该对象,还得把strongstr设置为nil:self.strongperson = nil;。这样输出结果才为 strongstr=(null),weakstr=(null)

  • 在给person类加了一个name属性。并用copy修饰 :(@property (nonatomic,copy) nsstring *name)。
nsstring *a = @"xiaoming";
person *p = [[person alloc] init];
p.name = a;
nslog(@"before p.name=%@",p.name);
a = @"xiaohua";
nslog(@"after p.name=%@",p.name);

输出结果:before p.name=xiaomingafter p.name=xiaoming。因为copy关键字修饰的属性是将对象拷贝一份赋值,所以你改变原对象并不会对拷贝后的对象有任何改变。

注:用@property声明 nsstringnsarraynsdictionary 经常使用copy关键字,是因为他们有对应的可变类型:nsmutablestringnsmutablearraynsmutabledictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份.

要点

  1. 可以用@property语法来定义对象中所封装的数据。

  2. 通过“修饰词”来指定存储数据所需的正确语义。

  3. 在设置属性所对应的实例变量时,一定要遵从该属性所声明的语义。

  4. 开发ios程序时应该使用nonatomic属性,因为atomic(同步锁)属性严重影响性能。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

  • ios uicollectionview实现横向滚动

    现在使用卡片效果的app很多,之前公司让实现一种卡片效果,就写了一篇关于实现卡片的文章。文章最后附有demo实现上我选择了使用uicollectionview ... [阅读全文]
  • iOS UICollectionView实现横向滑动

    本文实例为大家分享了ios uicollectionview实现横向滑动的具体代码,供大家参考,具体内容如下uicollectionview的横向滚动,目前我使... [阅读全文]
  • iOS13适配深色模式(Dark Mode)的实现

    iOS13适配深色模式(Dark Mode)的实现

    好像大概也许是一年前, mac os系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的终于, 随着iphone11等新手机的发售, ios 13系统... [阅读全文]
  • ios 使用xcode11 新建项目工程的步骤详解

    ios 使用xcode11 新建项目工程的步骤详解

    xcode11新建项目工程,新增了scenedelegate这个类,转而将原appdelegate负责的对ui生命周期的处理担子接了过来。故此可以理解为:ios... [阅读全文]
  • iOS实现转盘效果

    本文实例为大家分享了ios实现转盘效果的具体代码,供大家参考,具体内容如下demo下载地址: ios转盘效果功能:实现了常用的ios转盘效果,轮盘抽奖效果的实现... [阅读全文]
  • iOS开发实现转盘功能

    本文实例为大家分享了ios实现转盘功能的具体代码,供大家参考,具体内容如下今天给同学们讲解一下一个转盘选号的功能,直接上代码直接看viewcontroller#... [阅读全文]
  • iOS实现轮盘动态效果

    本文实例为大家分享了ios实现轮盘动态效果的具体代码,供大家参考,具体内容如下一个常用的绘图,主要用来打分之类的动画,效果如下。主要是ios的绘图和动画,本来想... [阅读全文]
  • iOS实现九宫格连线手势解锁

    本文实例为大家分享了ios实现九宫格连线手势解锁的具体代码,供大家参考,具体内容如下demo下载地址:效果图:核心代码://// clockview.m// 手... [阅读全文]
  • iOS实现卡片堆叠效果

    本文实例为大家分享了ios实现卡片堆叠效果的具体代码,供大家参考,具体内容如下如图,这就是最终效果。去年安卓5.0发布的时候,当我看到安卓全新的material... [阅读全文]
  • iOS利用余弦函数实现卡片浏览工具

    iOS利用余弦函数实现卡片浏览工具

    本文实例为大家分享了ios利用余弦函数实现卡片浏览工具的具体代码,供大家参考,具体内容如下一、实现效果通过拖拽屏幕实现卡片移动,左右两侧的卡片随着拖动变小,中间... [阅读全文]
验证码:
移动技术网