@property详解,@property修饰符以及各个修饰符区别(上)
相信很多参加过面试的人员很多都会被问到:weak与assign的区别,copy与strong的区别。如果你仅仅说一点点copy一般对NSString,weak对于控件的修饰,assign对于基本类型,那么面试官可以会对你深入问,block用过吗?修饰block用什么,又为什么用copy,这样一层层问下去,可能场面就很尴尬了,即使你进去,可能薪资也不能达到你所期望的。这篇我准备花几天完成,希望对大家有所帮助,阅读这篇问题大约需要20-30分钟……
一.@property
1.讲解
Objective-C的属性(property)是通过用@property定义的公有或者私有的方法。属性(property)提供了一种安全、便捷的方式来与这些属性(attribute)交互,而不需要手动编写一系列的访问方法,如果需要的话可以自定义getter和setter方法来覆盖编译器自动生成的相关方法。
尽量多的使用属性(property)而不是实例变量(attribute)因为属性(property)相比实例变量有很多的好处:
(1)自动合成getter和setter方法。当声明一个属性(property)的时候编译器默认情况下会自动生成相关的getter和setter方法更好的声明一组方法。
(2)因为访问方法的命名约定,可以很清晰的看出getter和setter的用处。
2.用法
@interface Person : NSObject
{
NSString *_name;
NSUInteger _age;
} - (void)setName:(NSString*)name;
- (NSString*)name;
- (void)setAge:(NSUInteger)age;
- (NSUInteger)age; @end @implementation Person - (void)setName:(NSString*)name {
_name = [name copy];
} - (NSString*)name {
return _name;
} - (void)setAge:(NSUInteger)age {
_age = age;
} - (NSUInteger)age {
return _age;
} @end
上述代码就是手动创建变量的getter
和setter
的实现,getter
和setter
本质就是符合一定命名规范的实例方法。
具体使用
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//函数调用name的setter
[p setName:@"Jiaming Chen"];
//函数调用age的setter
[p setAge:];
//函数调用name和age的getter,输出 Jiaming Chen 22
NSLog(@"%@ %ld", [p name], [p age]);
}
return ;
} //另一种写法
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//使用点语法访问name的setter
p.name = @"Jiaming Chen";
//使用点语法访问age的setter
p.age = ;
//使用点语法访问name和age的getter,输出 Jiaming Chen 22
NSLog(@"%@ %ld", p.name, p.age);
}
return ;
}
Objective-C允许使用点语法来访问getter
和setter
。
合成使用,使用@property方法
@interface Person : NSObject @property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age; @end @implementation Person //编译器会帮我们自动生成_name和_age这两个实例变量,下面代码就可以正常使用这两个变量了
@synthesize name = _name;
@synthesize age = _age; - (void)setName:(NSString*)name {
//必须使用_name来赋值,使用self.name来设置值时编译器会自动转为调用该函数,会导致无限递归
//使用_name则是直接访问底层的存储属性,不会调用该方法来赋值
//这里使用copy是为了防止NSMutableString多态
_name = [name copy];
} - (NSString*)name {
//必须使用_name来访问属性值,使用self.name来访问值时编译器会自动转为调用该函数,会造成无限递归
return _name;
} @end
二.@property后面有哪些修饰符
1.线程安全的-------atomic、nonatomic
atomic:原子性:Object-C使用的是一种线程保护技术,从基本上讲,是防止在未完成的时候被另一个线程更改,造成数据错误。而这种机制是耗费系统资源,所以在iPhone这种小型设备上,如果没有使用多线程之间的通讯编程,那么nonatomic是一个非常好的选择。
nonatomic:非原子性: 禁止多线程,变量保护,提高性能。
原子性:默认
这个属性是为了保证程序在多线程下,编译器会自动生成自旋锁代码,避免该变量的读写不同步问题,提供多线程线程,多线程只有一个线程对它访问。
attention
(1)atomic原子性指的是一个操作不可以被CPU中途叫停,然后再调度。即不能被中断,要么执行完,要么不执行。
(2)atomic是自旋锁,当上一个线程没有执行完毕的时候(被锁住),下一个线程会一直等待,不会进入睡眠,当上一个线程执行完毕后,下一个线程会立即执行。它区别于互斥锁,互斥锁在等待的时候,会进入睡眠状态,当上一个线程执行完毕之后,会被唤醒,然后再执行。
(3)atomic需要消耗大量的资源,执行效率低
但是atomic并不保证线程安全,因为只会保持set内部安全,在外面并不能保证安全!例如在数组array set方法中atomic原子操作,但是外面使用array addobject并不会保证线程安全!
二、访问权限
readwrite 默认 拥有getter/setter方法,可读可写
readonly 只读属性,只会生成getter方法,不会生成setter方法
三、内存管理
1.assign 默认
适用于基本数据类型:NSInteger,CGFloat和C数据类型int,float等,另外还有id类型
2.strong对应MRC中的retain
强引用,只有OC对象才能使用该属性,它使对象的引用计数加1
3.weak
弱引用,只是单纯引用某个对象,但是并未拥有该对象
即一个对象被持有无数个弱引用,只要没有强引用指向它,那么它就会被清楚释放。
下面通过一些拓展一下:面试重点!!!
(1)strong与retain
相同点:strong和retain都是针对对象类型进行内存管理。如果去修饰基本数据类型,Xcode会直接报错,当给对象类型使用此修饰符时,setter方法先将旧的对象属性release掉,再将新的对象赋值给属性并对该对象进行一次retain操作,两者都会增加对象的引用计数。
不同点:strong一般用于ARC,retain一般用于MRC环境。
(2)assgin与weak
相同点:assgin和weak不会牵扯到内存管理,不会增加引用计数
不同点:assign可修饰基本数据类型,也可修饰OC对象,但如果修饰对象类型指向的是一个强指针,当它指向的这个指针释放后,他仍指向这块内存,必须手动给置为nil,否则就会产生野指针,如果还通过此指针操作那块内存,便会导致EXC_BAD_ACCESS错误,调用了已经释放的内存空间;而weak只能修饰OC对象,且相比assign比较安全,如果指向的对象消失了,那么他会自动置为nil,不会产生野指针。
(3)strong与copy(重点重点)--这个可能比较难懂,多看两遍,可能有点乏味,不过很重要!!!
1.copy(拓展)----深拷贝和浅拷贝的区别
浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器加1;只是多了一个指向这块内存的指针,共用一块内存。
深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变;两块内存是完全不同的,也就是两个对象指针分别指向不同的内存,互不干涉。
判断是浅拷贝和深拷贝就看一下两个变量的内存地址是否一样,一样就是浅拷贝,不一样就是深拷贝,也可以改变一个变量的其中一个属性值看两者的值都会发生变化;
系统原生的对象深浅拷贝区别:
NSObject类提供了copy和mutableCopy方法,通过这两个方法即可拷贝已有对象的副本,主要的系统原生对象有:NSString和NSMutableString、NSArray和NSMutableArray、NSDictionary和NSMutableDictionary、NSSet和NSMutableSet。 NSValue和NSNumber 只遵守的NSCopying协议。
NSString-------copy/mutableCopy
NSString *string = @"copyTest";
NSString *copyString = [string copy];
NSString *mutableCopyString = [string mutableCopy];
NSMutableString *copyMutableString = [string copy];
NSMutableString *mutableCopyMutableString = [string mutableCopy];
NSLog(@"\n string = %p \n copystring = %p \n mutablecopystring = %p "
"\n copyMutableString = %p \n mutableCopyMutableString = %p \n",
string, copyString, mutableCopyString, copyMutableString, mutableCopyMutableString);
打印结果:
-- ::51.209346+ copy[:]
string = 0x100001040
copystring = 0x100001040
mutablecopystring = 0x10058c8e0
copyMutableString = 0x100001040
mutableCopyMutableString = 0x10058cde0
Program ended with exit code:
小结论:在字符串是直接赋值的,是否生成新对象是和=右边相关的,如果=右边的是mutableCopy才会产生新的对象
NSMutableString----copy/mutableCopy
NSMutableString *string = [NSMutableString stringWithString:@"学习研究"];
NSString *copyString = [string copy];
NSString *mutableCopyString = [string mutableCopy];
NSMutableString *copyMutableString = [string copy];
NSMutableString *mutableCopyMutableString = [string mutableCopy];
NSLog(@"\n string = %p \n copystring = %p \n mutablecopystring = %p "
"\n copyMutableString = %p \n mutableCopyMutableString = %p \n",
string, copyString, mutableCopyString, copyMutableString, mutableCopyMutableString);
打印结果:
-- ::44.755398+ copy[:]
string = 0x100504600
copystring = 0x100554e10
mutablecopystring = 0x100555880
copyMutableString = 0x100506e40
mutableCopyMutableString = 0x1005558f0
Program ended with exit code:
小结论:只要=右边从创建到赋值,至少包含一个MSMutable便会重新创建生成一个对象。
其他对象NSArray、NSMutableArray 、NSDictionary、NSMutableDictionary、NSSet、NSMutableSet一样适用。
刚刚疏解完copy,下面看一下面试最喜欢问的strong和copy修饰的区别:
下面看一组例子:
以NSString为例说明下,首先定义以下属性。
@property (nonatomic, strong) NSString *strongString;
@property (nonatomic, copy) NSString *copyedString;
@property (nonatomic, strong) NSMutableString *strongMutableString;
@property (nonatomic, copy) NSMutableString *copyedMutableString;
2.1 当外部赋给对应属性一个不可变(非mutable)的字符串 NSString
- (void)testPropertyCopyOrStrong
{
NSString *string = [NSString stringWithFormat:@"abc"];
self.strongString = string;
self.strongMutableString = string;
self.copyedString = string;
self.copyedMutableString = string;
string = [string stringByReplacingOccurrencesOfString:@"c" withString:@""]; NSLog(@"\n origin string: %p, %p %@ %@", string, &string, string, NSStringFromClass([string class]));
NSLog(@"\n strong string: %p, %p %@ %@", _strongString, &_strongString, _strongS tring, NSStringFromClass([_strongString class]));
NSLog(@"\n strongMutable string: %p, %p %@ %@", _strongMutableString, &_strongMutableSt ring, _strongMutableString, NSStringFromClass([_strongMutableString class]));
NSLog(@"\n copy string: %p, %p %@ %@", _copyedString, &_copyedString, _copyedS tring, NSStringFromClass([_copyedString class]));
NSLog(@"\n copyMutable string: %p, %p %@ %@", _copyedMutableString, &_copyedMutableSt ring, _copyedMutableString, NSStringFromClass([_copyedMutableString class])); }
打印结果:
origin string: 0x103a74098, 0x7fff5c18ca88 ab233 __NSCFString
strong string: 0xa000000006362613, 0x7f84c9f056d8 abc NSTaggedPointerString
strongMutable string: 0xa000000006362613, 0x7f84c9f056e8 abc NSTaggedPointerString
copy string: 0xa000000006362613, 0x7f84c9f056e0 abc NSTaggedPointerString
copyMutable string: 0xa000000006362613, 0x7f84c9f056f0 abc NSTaggedPointerString
可能大家不是很看懂这个例子:我们换一个简单的操作:
首先在类延展中声明两个属性变量:
@property (nonatomic, strong)NSString * stringStrong; //strong修饰的字符串对象
@property (nonatomic, copy)NSString * stringCopy; //copy修饰的字符串对象
接着创建两个不可变字符串(NSString)
//新创建两个NSString对象
NSString * strong1 = @"I am Strong!";
NSString * copy1 = @"I am Copy!";
将这两个属性进行赋值
//初始化两个字符串
self.stringStrong = strong1;
self.stringCopy = copy1;
分别打印四个变量的地址
StrongOrCopy[:] strong1 = 0x10a0b3078
StrongOrCopy[:] stringStrong = 0x10a0b3078
//这是两个字符串 StrongOrCopy[:] copy1 = 0x10a0b3098
StrongOrCopy[:] stringCopy = 0x10a0b3098
结果发现:可以看出,无论是strong修饰的字符串还是copy修饰的字符串,都进行了浅拷贝(仅仅是多了个指向该内存的指针,地址不会发挥变化)
如果创建两个不可变字符串对象(NSMutableString)呢
//新创建两个NSMutableString对象
NSMutableString * mutableStrong = [NSMutableString stringWithString:@"StrongMutable"];
NSMutableString * mutableCopy = [NSMutableString stringWithString:@"CopyMutable"];
分别对属性再次赋值
self.stringStrong = mutableStrong;
self.stringCopy = mutableCopy;
打印结果:
StrongOrCopy[:] mutableStrong = 0x7fccba425d60
StrongOrCopy[:] stringStrong = 0x7fccba425d60
StrongOrCopy[:] mutableCopy = 0x7fccba40d7c0
StrongOrCopy[:] stringCopy = 0x7fccba4149e0
结果发现:这时就发现了,用strong修饰的字符串依旧进行了浅Copy,而由copy修饰的字符串进行了深Copy,所以mutableStrong与stringStrong指向了同一块内存,而mutableCopy和stringCopy指向的是完全两块不同的内存。
看了一些实例,有什么用呢,看如下:
//新创建两个NSString对象
NSString * strong1 = @"I am Strong!";
NSString * copy1 = @"I am Copy!"; //初始化两个字符串
self.stringStrong = strong1;
self.stringCopy = copy1; //两个NSString进行操作
[strong1 stringByAppendingString:@""];
[copy1 stringByAppendingString:@""];
结果如下:
StrongOrCopy[:] strong1 = I am Strong!
StrongOrCopy[:] stringStrong = I am Strong!
StrongOrCopy[:] copy1 = I am Copy!
StrongOrCopy[:] stringCopy = I am Copy!
分别对在字符串后面进行拼接,当然这个拼接对原字符串没有任何的影响,因为不可变自字符串调用的方法都是有返回值的,原来的值是不会发生变化的.打印如下,对结果没有任何的影响:(不可变的字符串)
然后是对可变字符串进行操作:
//新创建两个NSMutableString对象
NSMutableString * mutableStrong = [NSMutableString stringWithString:@"StrongMutable"];
NSMutableString * mutableCopy = [NSMutableString stringWithString:@"CopyMutable"]; //初始化两个字符串
self.stringStrong = mutableStrong;
self.stringCopy = mutableCopy; //两个MutableString进行操作
[mutableStrong appendString:@"Strong!"];
[mutableCopy appendString:@"Copy!"];
打印结果如下:
StrongOrCopy[:] stringStrong = StrongMutableStrong!
StrongOrCopy[:] mutableStrong = StrongMutableStrong!
StrongOrCopy[:] stringCopy = CopyMutable
StrongOrCopy[:] mutableCopy = CopyMutableCopy!
对mutableStrong进行的操作,由于用strong修饰的stringStrong没有进行深Copy,导致共用了一块内存,当mutableStrong对内存进行了操作的时候,实际上对stringStrong也进行了操作; 相反,用copy修饰的stringCopy进行了深Copy,也就是说stringCopy与mutableCopy用了两块完全不同的内存,所以不管mutableCopy进行了怎么样的变化,原来的stringCopy都不会发生变化.这就在日常中避免了出现一些不可预计的错误。
总结:在不可变对象之间进行转换,strong与copy作用是一样的,但是如果在不可变与可变之间进行操作,那么楼主比较推荐copy,这也就是为什么很多地方用copy,而不是strong修饰NSString,NSArray等存在可变不可变之分的类对象了,避免出现意外的数据操作.
>>>>>>>拓展
修饰block为什么要用copy修饰?
关于block的用法,前几篇博客有,请关注:下面直接说原因:
(1)block内部没有调用外部局部变量时存放在全局区(ARC和MRC下均是)
(2)block使用了外部局部变量,这种情况也正是我们平时所常用的方式。MRC:Block的内存地址显示在栈区,栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.所以在使用Block属性时使用copy修饰。但是ARC中的Block都会在堆上的,系统会默认对Block进行copy操作
(3)用copy,strong修饰block在ARC和MRC都是可以的,都是在堆区
下面继续讲解
4.指定方法名称: setter= getter=
今天到此结束(如果太多,可能大家一时接受不了),下一步,我可能继续讲解block造成的循环引用,@property与ivar的区别等,看@property引出的那些问题
@property详解,@property修饰符以及各个修饰符区别(上)的更多相关文章
- Objective-C中的@Property详解
Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...
- 详解Google Chrome浏览器(操作篇)(上)
开篇概述 在上篇博客中详解Google Chrome浏览器(理论篇)一文中,主要讲解了Chrome 搜索引擎使用.Chrome安装和基本操作.Chrome 基本架构.多线程等原理性问题,这篇将重点讲解 ...
- SQL 中详解round(),floor(),ceiling()函数的用法和区别?
SQL 中详解round(),floor(),ceiling()函数的用法和区别? 原创 2013年06月09日 14:00:21 摘自:http://blog.csdn.net/yueliang ...
- Python之@property详解及底层实现介绍
转自:https://blog.csdn.net/weixin_42681866/article/details/83376484 前文 Python内置有三大装饰器:@staticmethod(静态 ...
- Python @property 详解
本文讲解了 Python 的 property 特性,即一种符合 Python 哲学地设置 getter 和 setter 的方式. Python 有一个概念叫做 property,它能让你在 Pyt ...
- Objective-c的@property 详解
转自:http://www.cnblogs.com/andyque/archive/2011/08/03/2125728.html 之前很多网友对我翻译的教程中的Property的使用感到有些迷惑不解 ...
- python3与django中@property详解
django提供了内置装饰器 @staticmethod\@classmethod\property 在OSQA中,@property的使用频率是非常高的.下面就是它的使用方法: @property ...
- OC中的@property详解
简介: @property 生成了变量的get set 方法,同时指定了变量名称. 例如@property (nonatomic,strong) NSString *name;表示生成了_name私有 ...
- python property详解
Python中有一个被称为属性函数(property)的小概念,它可以做一些有用的事情.在这篇文章中,我们将看到如何能做以下几点: 将类方法转换为只读属性 重新实现一个属性的setter和getter ...
随机推荐
- MySql技术内幕之MySQL入门(1)
目录 MySql技术内幕之MySQL入门(1) 安装 关于注释 执行SQL语句 关于命令大小写 创建数据库 查看表的信息 查看更加详细的信息 查看与给定模式相匹配的列 插入数据 利用insert添加行 ...
- Exp4 恶意代码分析 20154320 李超
恶意代码 概述 恶意代码是指故意编制或设置的.对网络或系统会产生威胁或潜在威胁的计算机代码.最常见的恶意代码有计算机病毒(简称病毒).特洛伊木马(简称木马).计算机蠕虫(简称蠕虫).后门.逻辑炸弹等. ...
- 所有子节点、Procedure、MySQL
在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点.但很遗憾,在MySQL的目前版本中还没有对应的功能. ...
- python之路(四)-set集合
set集合 set是一个无序且不重复的元素集合优点:访问速度快,解决重复问题 l1 = [1,2,34,5,6,7,4,3,3,] s2 = set(l1) #可以以列表元祖等作为参数传进来,set集 ...
- 0x66 Tarjan算法与无向图连通性(1)
……是什么? 给定无向连通图G=(V,E)(不一定连通); 割点:若对于x∈V,从图中删去节点x以及所有与x关联的边后,G分裂成两个或两个以上不相连的子图,则称x为G的割点. 桥(割边):若对于e∈E ...
- docker install
1.安装必要工具集 sudo yum install -y yum-utils 2.安装Docker官方源 sudo yum-config-manager \ --add-repo \ https:/ ...
- now
- web实验博客3.css-position
css-position属性为块级元素的定位提供了可能,今天我将讲述我所了解的position属性. div.h.p元素都可以称为块级元素,display属性可以改变其框类型,其值可以对框进行设置,默 ...
- Android中监控USB的插拔
一.需求 在Android应该开发过程中,用到了USB通讯,需要应用监控USB设备的插入和拔出,从而刷新USB设备列表. 二.实现 在使用时,需要register和unregister. 通过UsbD ...
- 微信屏蔽APP分享链接的解决方案原理,剖析微信域名防封技术
为什么很多商家在微信封域名如此严格的情况下,还会挤破头皮去做微信营销和推广呢?又有些人问,为什么别人的域名长时间推广都没事,自己的链接在微信内一推就被拦截呢?这里你可能需要注意一点事,事出无常必有妖. ...