iOS的property相关修饰符

原创 2015年11月18日 23:20:04

你如果参加过面试,80%的可能性会被问到过,@property的修饰符有哪些,区别又是什么;

既然想深入的了解我们就要知道它的来龙去脉,首先,我们的得知道什么是property。

Property

OC中称为属性,采用此属性,编译器会自动帮我们合成一个变量以及setter、getter方法,比如:

@property  NSString * name;

则系统会默认帮我们合成一个 NSString * _name;的成员,同时帮我们为此成员变量写好了setter、getter方法,因此,property的本质就是:

property = ivar + setter + getter

我们再看一下runtime.c的源码里关于类的结构定义:

struct objc_class {
        Class isa; // 指向metaclass

        Class super_class ; // 指向其父类
        const char *name ; // 类名
        long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取
        long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
        long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);
        struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址
        struct objc_method_list **methodLists ; // 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;
        struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率;
        struct objc_protocol_list *protocols; // 存储该类遵守的协议
    }

我们重点看一下ivars 和 methodLists,他们就是保存成员和方法的结构体,property其实就是往对应的list里添加了一个变量或一对方法罢了。

上面我们提到是默认添加,但是我们总是面对不同的软件需求或则特立独行的风格,比如,我不想让它的变量为_name,可以么?我不想让它的设置方法为 - (void)setName:(NSString *)name;可以么?或则我想让它只读?

这时候就引出了一部分property相关的修饰符了,通过这些修饰符,我们告诉编译器,怎么帮我们合成成员和方法,主要的有:

内存相关
  • strong
  • copy
  • weak
  • retain
  • assign
  • unsafe_unretained
线程安全相关
  • atomic
  • nonatomic
读写权限
  • readonly
  • readwrite
存取方法样式
  • setter=
  • getter=

    下面我们分别讲解一下:
    strong、weak、retain、assign
    strong的英文含义为强悍,强壮,这里是强引用的意思,它和weak一样,是ARC里引进的,它主要的作用是让属性的引用计数加1,可以认为retain≈strong;而weak却只会引用,并不会让引用计数加1,如果变对象释放,再次调用该变量对象,就会出现崩溃;当对象dealloc时候,weak修饰的变量对象指针自动赋值为nil;assign与weak类似,只是assign不会在对象dealloc时候自动将对应的属性设置为nil;
    以上都是理论:但是具体是怎么实现的呢?

我们用NSString * name 来解释(ARC下):

retain、strong:

  • (void)setName:(NSString *) name{
    _name = name;
    }
    上面的_name 对象其实修饰符默认为__strong,假设原来_name有值,那么原来的值指向的对象引用计数减一,新赋值的name对象计数加一;
@interface Test : NSObject
@property (nonatomic,strong) NSString *name;
@end
@implementation Test
@end

等效于

@interface Test : NSObject
{
    __strong NSString * _name;
}
@end
@implementation Test
-(void)setName:(NSString *)name
{
    _name = name;
}
//getter忽略
@end

我们开始说过property会合成一个ivar,这个ivar前面的修饰符,就是编译器通过@property括号里的内存修饰符而来。

再延生至MAC下,strong应该就变成:

-(void)setName:(NSString *)name
{
    if(_name != name){
       [_name  release];
       _name = [name retain];
    }
}
或
-(void)setName:(NSString *)name
{
       [name retain];
       [_name  release];
       _name = name;
}

assign实现便是:

-(void)setName:(NSString *)name
{
    _name = name;
}

那weak的实现是怎么样的呢?因为我们知道,他会在持有改属性的对象dealloc的时候自动将属性指针设置为nil,其实这个很好理解,因为每个实例的Class,Ivar list里的ivar,包含了成员变量的类型、名称,这个类型通过类型编码,转换成了一个char *的字符串表示,如上面的name会被编码为:

T@"NSString",&,N,V_name

那么,当遍历属性列时候,遇到weak修饰的对象,就设置为nil即可。

copy
这里我们单独将copy拿出来,这是为什么呢?因为它比较特别,特别在它使用的对象不同,结果也不同;从它的英文含义里,我们知道,它的意思是拷贝的意思,那拷贝的是指针还是指针指向的内容呢,如果你了解C++,这里就类似别名、引用拷贝,

    @property (copy) NSString * name;
    @property (copy) NSArray * fans;
    @property (copy) NSMutableArray * books;

@implement

NSString *theName = @"iOS2班";
self.name = theName;
NSLog(@"%p %p",_name,theName);

NSArray * fans = @[@"老谢"];
self.fans=fans;
NSLog(@"%p %p",_fans,fans);

NSMutableArray  * books =[@[ @"iOS开发进阶"] mutableCopy];
self.books = books;
NSLog(@"%p %p",_books,books);

@end

那上面这段代码,如果放在运行中,有什么问题呢?

2015-11-18 22:36:51.061 Demo[11559:2656983] 0x105c381a8 0x105c381a8
2015-11-18 22:36:51.063 Demo[11559:2656983] 0x7f8d29c28860 0x7f8d29c28860
2015-11-18 22:36:53.748 Demo[11559:2656983] 0x7f8d29c284b0 0x7f8d29c20b20

很明显,前两个不可变的对象,copy后,地址并未改变,而对于可变的对象,产生了一个新的数组对象,而且这个数组里面包含的对象并未改变。

我们在最后再加上这行代码:

NSLog(@"%@ %@",[_books class],[books class]);

运行结果为:

Demo[11609:2659420] __NSArrayI __NSArrayM

这个说明其实_books是一个不可变数组。
但是,我们明明在property中声明的对象为NSMutableArray * books;
这里完全是因为iOS系统的动态运行时,它并没有在编译的时候就确定你的对象类型,所以如果,你在代码后面执行譬如增删_books的方法,那肯定是要crash的。

刚才我们测试的都是iOS系统的类,假设我们用自定义的对象使用copy会如何呢?


ViewModel *model= [ViewModel new];
 self.model=model;

这时候你一定会遇上你非常熟悉的朋友:
[ViewModel copyWithZone:]: unrecognized selector sent to instance…….
这是说我们没有实现copyWithZone方法啊,这是什么意思?
这个方法是NSCopying协议里的方法,也就是如果我们自定义的类想用copy修饰,那必须实现这个协议。

因此,我们总结下:
1、对于系统的不可变对象,拷贝的只是指针,引用计数加1;
2、对于系统的可变对象,生成一个新的指针,指针指向的内容如果是容器,那容器里的东西不变,如果是可变字符串,那就是一个字符相等的地址不同的字符串,而且新的指针都是指向不可变对象,内容的应用计数加1(字符串是生成一个新的字符串,新的计数为1,但是那种常量字符串不存在引用计数的概念,请区别)。
3、对于自定义对象,那你想怎么实现就怎么实现,怎么计数就怎么计数了,完全取决于你的实现。

线程安全相关

atomic、nonatomic:
原子性和非原子性,其实这是一个加锁和不加锁的区别,加锁消耗性能,不加锁的话如果在多线程中,就有可能导致存取数据有差错,比如:
当我在A线程中写该属性值时候,在还没写完全,线程切换到了B,B线程进行了读该属性,那么读取的属性肯定也是不完全的。

那么这时候如果加上锁的话,在A没写完的情况下,B线程就会等待,知道A写完解锁,B才能进入读。

从上面我们细想一下,如果上面的atomic所谓的线程安全其实并不安全,因为它只针对读写的方法,对于整体来说,它并不安全:
比如ABCD,多个线程都进行self.name= xxx;的赋值,然后显示到label中,这时候,就可能很随机了,因为线程执行时间是不确定的。另外,如果这个属性是容器的话,多个线程对这个容器进行读写,容器里的对象并不线程安全,安全的也只是属性的设置。

//太晚了,先睡觉吧。。。

iOS 各种修饰符的区别汇总

atomic 设置成员变量的@property属性时,默认为atomic,提供多线程安全。 在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面...
  • u011366778
  • u011366778
  • 2015年07月27日 17:39
  • 1465

@property 后面可以有哪些修饰符?

1.线程安全的: atomic, nonatomic 2.访问权限的: readonly, readwrite 3.内存管理(ARC) assign,strong,weak,cop...
  • qq_32744055
  • qq_32744055
  • 2016年12月03日 12:39
  • 499

iOS-Objective-C的属性修饰符

在苹果引入了ARC之后,属性的也相对应的增加了一些修饰符。所以这里我们分别对MRC和ARC两种情况下的属性修饰符进行学习。其实主要的区别集中在对对象生命周期进行描述的属性修饰符会有所区别而已。...
  • linyousong
  • linyousong
  • 2016年02月28日 22:26
  • 1596

ios @property后面的修饰符

@property(nonatomic,copy) NSString *name; @property(nonatomic,strong) NSArray *dicArray; @property...
  • u011312237
  • u011312237
  • 2017年03月15日 11:01
  • 116

ios属性修饰符总结

ios属性修饰符总结 很多刚接触iOS的朋友对属性的@property的可选参数如何使用,什么情况下使用哪种选项不了解,也问了我很多这方面的知识,虽然知道怎么用,但是有些说不出其区别。在这里,再次深入...
  • woaifen3344
  • woaifen3344
  • 2015年12月08日 15:03
  • 5824

python @修饰符

Python 除了拥有实例方法外,还拥有静态方法和类方法,跟Java相比需要理解这个类方法的含义。 class Foo(object):      def test(self)://定义了实...
  • jincm13
  • jincm13
  • 2014年08月27日 09:14
  • 931

Objective-C语言--属性修饰符

Objective-C属性修饰符
  • AddyChen
  • AddyChen
  • 2014年09月25日 12:38
  • 3271

OC常用属性修饰符使用总结

内存管理一直是OC的重点内容,而属性的内存管理从我们开始编写第一句iOS代码就伴随着我们,其重要程度可见一斑。下面是OC内存管理修饰符的总结,只是写出最常用的几个属性的内存管理修饰符,适用范围ARC(...
  • jb448372210
  • jb448372210
  • 2015年07月16日 20:33
  • 943

iOS基础之属性修饰符的区别

声明变量的修饰符:__strong, __weak, __unsafe_unretained, __autoreleasing 声明属性的修饰符:strong, weak, unsafe_un...
  • q1194259339
  • q1194259339
  • 2016年07月25日 11:11
  • 414

C# 中方法、类的默认修饰符

C# 方法默认访问级别 : private  C# 类默认访问级别 : internal  C# 方法默认访问级别 : private C# 类默认访问级别 : internal 1.命名空...
  • qq_27361571
  • qq_27361571
  • 2016年03月15日 17:10
  • 3396
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:iOS的property相关修饰符
举报原因:
原因补充:

(最多只允许输入30个字)