现在的位置: 首页 > 综合 > 正文

属性与内存管理

2018年01月23日 ⁄ 综合 ⁄ 共 7607字 ⁄ 字号 评论关闭
  1. <span style="font-size:18px;">  
  2. 属性与内存管理(属性与内存管理都是相互关联的)第一部分  
  3.   
  4. 一,属性:  
  5.   
  6. 属性是OC2.0之后出来的新语法,用来代替settergetter方法,使用属性可以快速创建setter以及getter方法的声明,settergetter方法的实现,另外添加了对实例变量操作的安全处理(其安全是通过内存管理实现的)  
  7.   
  8. setter 方法作用:为单一的实例变量重新赋值, 规范: (- 号方法)无返回值, 名字以set开头后面加上要设置的实例变量的名字,该方法有且只有一个参数,参数的类型和实例变量类型相同  
  9.   
  10. getter 方法作用:获取某一单一变量的值, 规范: (- 号方法)有返回值,返回值类型与要获取的实例变量的类型相同,名字与要获取的实例变量的名字相同,无参数  
  11.   
  12.  实例变量依托于对象存在,为对象开辟空间是根据实例变量的空间大小开辟的  
  13.   
  14.    
  15.   
  16. 1,属性的三大特性 (关于语义特性的具体用法此处不再详细叙述,下面会解说)  
  17.   
  18.          第一大特性:  
  19.   
  20.          (1),readonly:告诉编译器,属性在自动生成方法时,只会生成getter方法,不会生成setter方法  
  21.   
  22.          (2),readwrite:告诉编译器,输出在自动生成方法时,既要生成getter方法,也要生成setter方,系统默认的读写特性  
  23.   
  24.          第二大特性: 原子特性  
  25.   
  26.          (1),atomic:原子特性,保证线程安全,内部做了安全处理(加锁与解锁)  
  27.   
  28.          (2),nonatomic:非原子特性,不保证线程安全,因为对于gettersetter方法的使用比较频繁,在一段时间内可能要访问多次,使用atomic会非常消耗系统资源,降低程序的执行效率,使用nonatomic虽然不保证线程安全,但是使用一般情况下是安全的,因此对于原子特性通常使用nonatomic  
  29.   
  30.          第三大特性:语义特性  
  31.   
  32.          (1),assign:直接赋值,适用于基本数据类型,也可适用于对象类型,系统默认的语义特性  
  33.   
  34.          (2),copy:适用于对象类型,并且要服从NSCopying协议的对象,会复制出一个新的对象,拥有新的对象所有权,(引用计数 + 1)(暂时这样理解内存管理会详细介绍),  
  35.   
  36.          (3),retain:适用于对象类型,会造成对象的引用计数 + 1;  
  37.   
  38.          2,属性的定义  
  39.   
  40.          @property(关键字,用来定义属性) + 属性的特性 + 属性的类型 (和实例变量类型相同)+ 属性名 (和实例变量名相同);  其中@property.h文件中只是自动声明settergetter方法的声明  
  41.   
  42.          3,在.m文件中  
  43.   
  44.          @synthesize(关键字)  属性的实现部分,实现属性自动生成的settergetter方法,如果在.m文件中通过@synthesize对属性进行合成,系统会自动合成,只不过此时系统默认的settergetter方法内部操作的实例变量是_属性名,我们通常情况下在.m文件中有关gettersetter的方法都是什么都不写  
  45.   
  46.          (1),如果指定的实例变量在.h文件里没有定义,系统会自动生成指定的实例变量,但是生成的实例变量是私有的,子类不可以直接访问,如果实例变量想让子类继承,则必须在.h文件中定义实例变量.如果属性的实现部分没有指定内部所要操作的实例变量时,系统会自动生成一个和属性名一样的实例变量  
  47.   
  48.          (2),如果对于settergetter方法我们已经实现了系统就不会再自动生成  
  49.   
  50.          (3),如果在.m文件中通过@synthesize对属性进行合成,系统会自动合成,只不过此时系统默认的settergetter方法内部操作的实例变量是_属性名  
  51.   
  52.    
  53.   
  54. 下面举个Person类的例子说明  
  55.   
  56.  (1), 在.h文件中  
  57.   
  58. @interface Person :NSObject  
  59.   
  60.          
  61.   
  62.         //这里定义实例变量是位了让子类继承,如果不写,系统默认生成的是私有的实例变量,(当然如果没有子类这里完全可以不写)  
  63.   
  64.         {  
  65.   
  66.               
  67.   
  68.             NSString*_name; //姓名  
  69.   
  70.             NSString *_sex;  //性别  
  71.   
  72.             NSInteger _age; //年龄  
  73.   
  74.             NSString *_job;//工作  
  75.   
  76.         }  
  77.   
  78.          
  79.   
  80.  //定义属性将name的语义特性声明为retain,sex的语义特性声明为copy,job的语义特性声明为assign(系统默认的时assign,这里可以不写)  
  81.   
  82. @property (nonatomic,retainNSString *name;@property (nonatomiccopyNSString *sex;  
  83.   
  84. @property (nonatomic) NSInteger age;  
  85.   
  86. @property (nonatomicNSString *_job;  
  87.   
  88.    
  89.   
  90.          
  91.   
  92.          
  93.   
  94. @end  
  95.   
  96.    
  97.   
  98. (2), 在.m文件中  (把不同特性的settergetter方法的内部实现详细的写出来,以便清楚的了解系统内如生成的代码)  
  99.   
  100. 在实现方法之前首先说明,在实现settergetter方法时,内部绝对不可以出现self.+属性名.因为这样写相当于自己调用自己,会形成死循环  
  101.   
  102. 1), 把name语义特性声明为retain时,settergetter方法的内部实现  
  103.   
  104. setter方法:  
  105.   
  106. - (void)setName:(NSString *)name  
  107.   
  108. {  
  109.   
  110.     if (_name != name) {  
  111.   
  112.         [_namerelease];  
  113.   
  114.         _name = [name retain];  
  115.   
  116.     }  
  117.   
  118. }  
  119.   
  120.    
  121.   
  122. getter方法:  
  123.   
  124. - (NSString *)name  
  125.   
  126. {  
  127.   
  128.     return [[_nameretain] autorelease];  
  129.   
  130. }  
  131.   
  132.    
  133.   
  134.    
  135.   
  136. 2),把性别sex语义特性声明为copy时,settergetter方法的内部实现  
  137.   
  138.    
  139.   
  140. setter方法:  
  141.   
  142. - (void)setSex:(NSString *)sex  
  143.   
  144. {  
  145.   
  146.     if (_sex != sex) {  
  147.   
  148.         [_sexrelease];  
  149.   
  150.         _ sex = [sex copy];  
  151.   
  152.     }  
  153.   
  154. }  
  155.   
  156.    
  157.   
  158. getter方法:  
  159.   
  160.    
  161.   
  162. - (NSString *)sex  
  163.   
  164. {  
  165.   
  166.     return [[_sexretain] autorelease];  
  167.   
  168. }  
  169.   
  170.    
  171.   
  172. 3),把job语义特性声明为assign时,settergetter方法的内部实现  
  173.   
  174.    
  175.   
  176. setter方法:  
  177.   
  178. - (void)setJob:(NSString *)job  
  179.   
  180. {  
  181.   
  182.    _job = job)  
  183.   
  184. }  
  185.   
  186.   
  187. getter方法:  
  188.   
  189. - (NSString *)job  
  190.   
  191. {  
  192.   
  193.     return_job;  
  194.   
  195. }  
  196.   
  197.   
  198. 4), age默认的语义特性为assign时,settergetter方法的内部实现  
  199.   
  200.    
  201.   
  202. setter方法:  
  203.   
  204. - (void)setAge:(NSInteger)age  
  205.   
  206. {  
  207.   
  208.    _age != age)  
  209.   
  210. }  
  211.   
  212.    
  213.   
  214. getter方法:  
  215.   
  216. - (NSString *)age  
  217.   
  218. {  
  219.   
  220.     return_age;  
  221.   
  222. }  
  223.   
  224. 这里这样写的原因下面内存管理介绍  
  225.   
  226. 二,  
  227.   
  228.  (一),内存管理简介:  
  229.   
  230. 1,iOS应用程序出现Crash(闪退),90%以上的原因是内存问题。在一个拥有  
  231.   
  232.          数十个甚至是上百个类的工程里,查找内存问题极其困难。了解内存常  
  233.   
  234.          见问题,能帮我们减少出错几率。  
  235.   
  236. 2,内存问题体现在两个方面:内存溢出、野指针异常。了解内存管理,能帮我  
  237.   
  238.          们提升程序性能,大大减少调试bug时间。  
  239.   
  240. 3,内存管理机制分为三种:  
  241.   
  242. (1),垃圾回收(gc)  
  243.   
  244.          垃圾回收:程序员只需要开辟内存空间,不需要用代码显示地释  
  245.   
  246.          放,系统来判断哪些空间不再被使用,并回收这些内存空间,以便再  
  247.   
  248.          次分配。整个回收的过程不需要写任何代码,由系统自动完成垃圾回  
  249.   
  250.          收。Java开发中一直使用的就是垃圾回收技术        
  251.   
  252.  (2),MRC(Manual Reference Count)人工引用计数:  
  253.   
  254.          内存的开辟和释放都由程序代码进行控制。相对垃圾回收来说,对内存  
  255.   
  256.          的控制更加灵活,可以在需要释放的时候及时释放,对程序员的要求较  
  257.   
  258.          高,程序员要熟悉内存管理的机制  
  259.   
  260. (3),ARCAuto Reference Count)自动引用计数:  
  261.   
  262.          iOS 5.0的编译器特性,它允许用户只开辟空间,不用去释放空间。  
  263.   
  264.          它不是垃圾回收!它的本质还是MRC,只是编译器帮程序员默认加了释放  
  265.   
  266.          的代码。  
  267.   
  268.  4,对于iOS支持两种内存管理方式:ARC和MRC  
  269.   
  270.          C语言中,使用malloc和free,进行堆内存的创建和释放。堆内  
  271.   
  272.          存只有正在使用和销毁两种状态。实际开发中,可能会遇到,两个以上  
  273.   
  274.          的指针使用同一块内存。C语言无法记录内存使用者的个数。  
  275.   
  276.          而OC采用引用计数机制管理内存,当一个新的引用指向对象  
  277.   
  278.          时,引用计数器就递增,当去掉个引用时,引用计数就递减,当  
  279.   
  280.          引用计数到零时,该对象就会释放占有的资源  
  281.   
  282.  (二),内存管理基本原则:  
  283.   
  284.          如果对一个对象进行alloc,retain,copy之后,就拥有了该对象的使用权,就必须  
  285.   
  286.          对该对象进行release或者autorelease.即(谁使用+1操作,谁就要进行-1操  
  287.   
  288.          作)  
  289.   
  290. 1,将OC里只能利用以下五种方法对引用计数改变:  
  291.   
  292. (1),alloc:(+号方法)(与dealloc对应) 开辟堆区的内存空间,将对象的引用计数由0 变 1(+ 1操作);  
  293.   
  294.  (2),copy:(-号方法)  重新开辟空间与要拷贝的对象(即空间)开辟的空间大小一样,里面存储的内容也完全一样,只是地址不同,引用计数由0 到 1(+ 1操作,这里不是对原对象的引用计数 + 1,而是对新拷贝的对象+ 1);  
  295.   
  296. (3),retain:(-号方法)与release对应使用对象的引用计数 + 1(操作后立即 + 1),  
  297.   
  298. (4),release:(-号方法)与retain对应使用对象的引用计数 - 1(操作后立即 - 1),  
  299.   
  300. (5),autorelease:(-号方法) 对象的引用计数 - 1(不会立即 – 1,会在未来的某一时刻引用计数 – 1),只要有对象使用autorelease操作,就必须有对应的自动释放池autoreleasePool{},自动释放池工作原理:它会将声明为autorelease的对象放入离它最近的自动释放池中,当自动释放池销毁时,会向池中的每个对象发送一个release消息,因此使用autorelease实质的-1操作不是autorelease进行的还是release执行的  
  301.   
  302. 2,retainCount 用来获取当前对象的引用计数(针对自定义的类,系统的类我们不需要了解,因为其内部做了好多操作),  
  303.   
  304. 3,dealloc: 回收空间(与alloc对应)当该类型的对象引用计数为0时, 系统会自动调用dealloc方法来回收空间,该方法不需要手动调用,  
  305.   
  306. 4,当对象的引用计数为变为0时,不能在访问该对象,即不要对该对象做任 何操作,如果继续操作就会出现野指针问题,这时可能会写到某行代码时突 然crash(即使是没有写任何与引用计数相关的代码),因为此时系统已经自动回收了指针变量指向的空间,该指责你变量已经没有使用被收回的空间的权限了,也就不能访问没有权限的对象.  
  307.   
  308. 5,查找系统因为野指针问题崩溃的时哪一行代码: 在菜单栏上点击Product ->Scheme ->Edit Scheme->选中Objective-c那行的Enable Zomible Object 和 Debugger下面的最后一行,重新运行,即可找到哪里出错了.  
  309.   
  310. 6, 当写完和引用计数相关的代码后程序立即crash,是因为系统被回收后又使用了引用计数 - 1有关的操作,这时应该将其删除  
  311.   
  312. 7,我们平时要养成随时在对象后面记录对象当前的引用计数,以减少出错的概率  
  313.   
  314. 8,验证对象空间是否被回收,只有查看该类的dealloc方法有没有执行即可  
  315.   
  316. 三,下面仍以Person为例具体介绍  
  317.   
  318. settergetter方法,内部是对实例变量赋值以及实例变量的取值操作,所有方法内部要操作实例变量  
  319.   
  320. 1,把name语义特性声明为retain时,settergetter方法的内部实现  
  321.   
  322. setter方法:  
  323.   
  324. - (void)setName:(NSString *)name  
  325.   
  326. {  
  327.   
  328.     if (_name != name) {  
  329.   
  330.         [_namerelease];  
  331.   
  332.         _name = [name retain];  
  333.   
  334.     }  
  335.   
  336. }  
  337.   
  338. getter方法:  
  339.   
  340. - (NSString *)name  
  341.   
  342. {  
  343.   
  344.     return [[_nameretain] autorelease];  
  345.   
  346. }  
  347.   
  348. (1), if (_name != name)判断条件目的: 判断原有对象和新对象是否是同一个,如果是同一个就没必要重新赋值了,否则会先release, release后空间就被系统回收了,此时若再retain就会出现野指针问题  
  349.   
  350. (2), [_namerelease]操作的目的: 释放保有的之前对象的所有权,若不释放会造成内存泄露,因为前一个对象已经不再使用了  
  351.   
  352. (3), _name = [name retain]操作的目的: 让实例变量保有新的对象所有权,retain解决了野指针问题  
  353.   
  354. 2,把性别sex语义特性声明为copy时,settergetter方法的内部实现  
  355.   
  356.  setter方法:  
  357.   
  358. - (void)setSex:(NSString *)sex  
  359.   
  360. {  
  361.   
  362.     if (_sex != sex) {  
  363.   
  364.         [_sexrelease];  
  365.   
  366.         _ sex = [sex copy];  
  367.   
  368.     }  
  369.   
  370. }  
  371.   
  372. 针对setter方法  
  373.   
  374. (1), if (_sex != sex)判断条件目的: 判断原有对象和新对象是否是同一个,如果是同一个就没必要重新赋值了,否则会先release, release后空间就被系统回收了,此时若再retain就会出现野指针问题  
  375.   
  376. (2), [_sexrelease];操作的目的: 释放保有的之前对象的所有权,若不释放会造成内存泄露,因为前一个对象已经不再使用了  
  377.   
  378.  (3), _ sex = [sex copy];此处的copy操作跟上面的retain操作有所不同,copy是把sex指向的空间复制一份(即重新开辟一个空间,大小跟sex指向的空间大小一样,空间里存储的数据都相同),的操作的目的:让实例变量保有新的对象所有权,retain解决了野指针问题,  
  379.   
  380. getter方法:  
  381.   
  382. - (NSString *)sex  
  383.   
  384. {  
  385.   
  386.     return [[_sexretain] autorelease];  
  387.   
  388. }  
  389.   
  390. 3,把job语义特性声明为assign时,settergetter方法的内部实现  
  391.   
  392. setter方法:  
  393.   
  394. - (void)setJob:(NSString *)job  
  395.   
  396. {  
  397.   
  398.    _job = job)  
  399.   
  400. }  
  401.   
  402.  这里由于job的语义特性为assign,所以其内部的操作是直接赋值的方式  
  403.   
  404.  getter方法:  
  405.   
  406. - (NSString *)job  
  407.   
  408. {  
  409.   
  410.     return_job;  
  411.   
  412. }  
  413.   
  414. 4, age默认的语义特性为assign时,settergetter方法的内部实现  
  415.   
  416. setter方法:  
  417.   
  418. - (void)setAge:(NSInteger)age  
  419.   
  420. {  
  421.   
  422.    _age != age)  
  423.   
  424. }  
  425.   
  426. 这里age为基本数据类型,为它开辟的空间是在栈区的,除了堆区需要手动管理,其他的内存区域都是系统管理,对基本数据类型系统默认的语义特性为assign, 所以其内部的操作也是是直接赋值的方式  
  427.   
  428. getter方法:  
  429.   
  430. - (NSString *)age  
  431.   
  432. {  
  433.   
  434.     return_age;  
  435.   
  436. }  
  437.   
  438.    
  439. </span>  

抱歉!评论已关闭.