iOS 属性
@property
- property = ivar(变量) + set方法 + get方法
我们每次在增加一个属性,系统都会在
ivar_list
中添加一个成员变量的描述,在method_list
中增加setter
与getter
方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出setter
与getter
方法对应的实现,在setter
方法中从偏移量的位置开始赋值,在getter
方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
@property (nonatomic, strong)UIView *bgView;
当使用
@property
声明一个bgView
的属性时,在编译阶段,编译器会自动
给对象bgView
添加一个实例变量_bgView
以及它的set
方法和get
方法
代码验证
#import "ViewController.h"
#include <objc/runtime.h>
@interface ViewController ()
@property (nonatomic, strong)UIView *bgview;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
unsigned int count = 0;
Ivar *varList = class_copyIvarList([self class], &count);
for (unsigned int i = 0; i < count; i++) {
const char *varName = ivar_getName(varList[i]);
printf("成员变量-------%s\n",varName);
}
Method *methodList = class_copyMethodList([self class], &count);
for (unsigned int i = 0; i < count; i++) {
SEL methodName = method_getName(methodList[i]);
NSLog(@"方法-------%@",NSStringFromSelector(methodName));
}
}
@end
打印日志
成员变量-------_bgview
2021-03-31 19:19:40.612196+0800 basicLearn[12511:760231] 方法-------bgview
2021-03-31 19:19:40.612294+0800 basicLearn[12511:760231] 方法-------setBgview:
2021-03-31 19:19:40.612378+0800 basicLearn[12511:760231] 方法-------.cxx_destruct
2021-03-31 19:19:40.612465+0800 basicLearn[12511:760231] 方法-------viewDidLoad
- 如果我们想自定义属性的存取方法,那么需要注意以下几点
1.同时自定义set
和get
方法,会导致property
失效
image.png
2.而如果我们重写了set
方法,编译器会自动生成get
方法,反过来重写了get
方法,编译器也会自动生成set
方法
@synthesize
- 当我们需要同时重写属性
bgView
的set
和get
方法时,可以用synthesize
声明属性 - 当我们用
synthesize
声明了属性,但是没有重写set
和get
方法,编译器还是会自动补全存取方法的
代码展示
1.
bgView
是属性,而_bgView
是成员变量
2.@synthesize bgView = _bgView;
这个方法是告诉编译器bgView
属性是_bgView
实例变量生成setter
和getter
方法的实现
@interface ViewController ()
@property (nonatomic, strong)UIView *bgView;
@end
@implementation ViewController
@synthesize bgView = _bgView;
- (void)setBgView:(UIView *)bgView {
_bgView = bgView;
}
- (UIView *)bgView {
return _bgView;
}
@dynamic
-
@dynamic
是告诉编译器 属性 的setter
方法 和getter
方法 由用户自己实现 - 但要注意的是 如果用@dynamic声明一个属性,但没有手动实现
setter
和getter
方法,编译时是不会有问题的,但代码运行时调用了属性的set
方法或者get
方法,程序就会崩溃 - 还有一个应用:当有一个
Father
类,里面有一个str
的属性,这时创建了一个Son
类继承了Father
类,在Son
类里同时也声明一个str
的属性,这时如果我们重写子类Son
里的str
属性,就会报错,因为我们同时在Father
和Son
类里声明了str
属性,系统默认是会在父类Father
里合成str
的存取方法,这时我们就需要在子类Son
类里用@dynamic
修饰str
,告诉编译器在子类里不用去合成str
属性的存取方法
读写权限 readonly readwrite
- 如果在声明属性时,没有说明是
readonly
,那么默认都是readwrite
- 如果属性被声明为
readonly
,那么我们获取调用该属性的get
方法,调用set
方法会报错
@property (nonatomic, copy,readonly)NSString *str;
readonly
可以应用在 父类的.h文件
将属性设置为readonly
,在.m文件
又设置为readwrite
,那么继承该父类的子类只能获取这个属性而不能修改它的值,只有父类才有权利去修改这个属性的值
原子性 nonatomic atomic
-
atomic
和nonatomic
修饰属性时生成的setter
和getter
方法是不一样的
atomic修饰属性会在属性的存取方法里加上的同步锁
🔒@synchronized(self)
,同步锁()里的self可以被其他对象代替,内是根据括号内的内容来判断是否是同一个同步代码块,比如
当两个并发线程访问同一个对象object中的这个synchronized(self)
同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。 -
atomic
里的set
方法和get
方法都加了同步锁🔒,这样可以保证 读写 功能的完成性,但是不能保证对象的线程安全,比如:线程B,C同时调用属性的set
方法,线程A去调用get
方法,那么get
到的值有可能是B设置的,有可能是C设置的,也有可能是B,C之前设置的 -
nonatomic
这个的set
和get
方法没有加锁,因此不能保证set
和get
操作的完成性,因此get
出的属性可能刚被释放掉,也可能是不完整的,但是由于没有加锁,访问的速度会相对快,性能的消耗也相对少
strong,weak,assign,retain,copy
- strong
strong 表示强引用,用于修饰NSObject对象,不能修饰基础数据类型,
strong修饰的对象被引用时 引用计数会➕1,当该对象的引用计数为0时,该对象会被释放掉
- weak
weak 表示弱引用,用于修饰NSObject对象,不能修饰基础数据类型,主要用于避免循环引用,要注意的是:weak修饰的对象才被初始化之后是会释放掉的,对象释放之后所有指向它的指针都会自动置为nil,这样可以防止野指针的问题
//bgView是weak修饰的
//这样初始化之后bgView就会被释放掉
self.bgView = [[UIView alloc]init];
- assign
assign 可以同时用来修饰基础数据类型(CGFloat等)和对象,当用assign修饰对象时,作用和weak相似,但是对象被释放之后,不会自动将属性置为nil,那么容易出现野指针的问题
- copy
copy 作用类似与strong,修饰NSObject对象,不能修饰基础数据类型,当copy修饰的属性是可变时(NSMutable),copy后的对象,会变成不可变对象,(比如从NSMutableArray变成NSArray,那么调用增删操作时会引发crash,因为尽量不要用copy修饰可变对象),指针地址跟之前是不一样的,也就是说会重新分配一块新的内存空间
- retain
一般用于MRC模式下,修饰对象引用计数会加1,后来在ARC模式下基本都使用strong了