内存管理(3)

来源:互联网 发布:移动网络玩游戏卡吗 编辑:程序博客网 时间:2024/06/05 23:57

10、【掌握】@property参数(一)

  • @property 4.4前

    • 1) @property + 手动实现
    • 2) @property int age; + @synthesize age;//get和set方法的声明和实现都帮我们做了
    • 3) @property int age + @synthesizes age = _b;
  • @property 4.4增强 .h

  • @property int age;
    • 1) 生成_age
    • 2) 生成_age的get和set方法的声明
    • 3) 实现_age的get和set方法

1.@property参数

  • 格式:@property (参数1,参数2) 数据类型 方法名
// 基本数据类型int _age;// set方法的写法-(void)setAge:(int) age{_age = age;}// 用assign修饰后,仍旧会生成以上标准的set方法@property (assign) int age;// oc对象类型@property (retain) Car *car ;// 用retain修饰后,生成如下内存管理代码-(void)setCar:(int) car{   if(_car ! = car)   {      [_car release];      _car = [car retain];  }}
  • 1.内存管理相关参数

    • retain : release旧值,retain新值(用于OC对象)
    • assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)
    • copy : release旧值,copy新值(一般用于NSString *)
  • 验证assign如果作用在对象上,实际上就是直接赋值

//使用@property增强型 生成get和set方法@property(nonatomic,assign)Car *car;//.m文件中实际上生成的是- (void)setCar:(Car *)cat{   _car = car; //当对象release后,将无法使用该对象}// main函数int main(){ Person *p = [Person new]; Car *c = [Car new]; // 给人一辆车 p.car = c; // 释放车 [c release]; // 让人开车 [p drive]; //此时p指向了僵尸对象}
  • 使用@property增强型 生成get和set方法
//使用@property增强型 生成get和set方法@property(nonatomic,retain)Car *car;//.m文件中实际上生成的是- (void)setCar:(Car *)cat{   if(_car != car)   {     [_car release];     _car = [car retain];   }}// main函数int main(){ Person *p = [Person new]; Car *c = [Car new]; // 给人一辆车 p.car = c; // 释放车 [c release]; // 让人开车 [p drive];}
  • 此时,会发生内存泄露。
  • 解决办法
- (void)dealloc{    NSLog(@"Person -- dealloc");    [_car release];    [super dealloc];}

11.【掌握】@property参数(二)

1.@property 参数(二)

  • 1、是否要生成set方法(若为只读属性,则不生成)

    • readonly:只读,只会生成get的声明和实现
    • readwrite:默认的,同时生成set和get的声明和实现
  • 2.多线程管理(苹果在一定程度上屏蔽了多线程操作)

    • nonatomic:高性能,一般使用这个
    • atomic:低性能,默认

    • atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。

  • 3.set和get方法的名称

    • 修改set和get方法的名称,主要用于布尔类型。因为返回布尔类型的方法名一般以is开头,修改 名称一般用在布尔类型中的getter。
    控制set方法和get方法的名称setter : 设置set方法的名称,一定有个冒号:getter : 设置get方法的名称@property(nonatomic,assign, setter=abc:,getter=haha)int age
  • 可以理解为:

    • [p setAge: ]——> [p abc:],
    • [p age] ———> [p haha];
    • p.age 不会报错(内部优化)

    “`objc
    @property(nonatomic,assign, setter=setVip:,getter=isVip) BOOL vip;

![](http://img.blog.csdn.net/20161009213635575)* 用法: 设置值![](http://img.blog.csdn.net/20161009213720825)* 获取值![](http://img.blog.csdn.net/20161009213749107)##12、【理解】应用:电商App练习####电商App类的设计* 要求:利用OC+面向对象设计下面的三个类:* 一、商品类-Goods * 属性: 商品名称    * 单价    * 重量    * 商品展示图片    * 生产日期(暂时用结构体表示)produceDate    * 过期日期 expireDate* 二、买家类(用户) Buyer * 属性:   * 姓名   * 性别(枚举)   * 年龄   * 身高(单位:cm)* 三、卖家类 - Seller * 属性:   * 姓名   * 性别(枚举)   * 年龄   * 身高(单位:cm)   * 所出售商品(假设一个卖家就卖一件商品)* 四、买家类、卖家类(抽象父类Person)* 五、在main函数中创建卖家、商品、买家类的对象。```objc// 创建一个表示日期时间的结构体typedef struct{  int hour;  int min;  int sec;}Time;//表示日期typedef struct{  int year;  int month;  int day;  Time time; }MyDate;MyDate produceDate = (MyDate){2011, 9, 10, {15, 16, 30}}; //注意,这句话是错误的<div class="se-preview-section-delimiter"></div>

  • 原因是:
    • goods.expireDate—–>得到这个成员expireDate的值,是结构体类型
    • expireDate.Time 这是访问结构体变量,time的值
    • goods.expireDate.time 会被误认为是点语法,导致出错
// 创建一个表示性别的枚举typedef enum{  GenderMale, // 男  GenderFemale // 女} Gender;
![](image1/12.1.png)* 原因是: * goods.expireDate----->得到这个成员expireDate的值,是结构体类型 * expireDate.Time 这是访问结构体变量,time的值 * goods.expireDate.time 会被误认为是点语法,导致出错```objc// 创建一个表示性别的枚举typedef enum{  GenderMale, // 男  GenderFemale // 女} Gender;<div class="se-preview-section-delimiter"></div>

13、【理解】@class的使用

  • 场景

1.@class的使用

  • 作用
    • 可以简单地引用一个类
  • 简单使用
    • @class Dog; //类的引入
    • 仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容
  • 具体使用

    • 在.h文件中使用@class引用一个类
    • 在.m文件中使用#import包含这个类的.h文件
  • 如下面代码:

  A.h文件 #import "B.h" @interface A : NSObject  {     B *b;  }  @end
  • 为了简单起见:A类是引用类,B类是被引用类,这里先不考虑A类的实现文件。
  • 通常引用一个类有两种办法:

    • 一种是通过#import方式引入;
    • 另一种是通过@class引入;
  • 这两种的方式的区别在于:

    • 1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在 A.h文件中 B *b只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息;

    • 2)使用@class方式由于只需要知道被引用类(B类)的名称就可以了,而在实现类由于要用到被引用类中的实体变量和方法,所以需要使用#import来包含被引用类的头文件;

    • 3)通过上面2点也很容易知道在编译效率上,如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt(A->B, B->C,C->D…),一旦最开始的头文件稍有改动,后面引用到这个文件的所有类 都需要重新编译一遍,这样的效率也是可想而知的.而相对来讲,使用@class方式就不会出现这种问题了;

    • 所以:我们实际开发中尽量在.h头文件中使用@class

    • 4)对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类,B类的代码:
  • 当程序运行时,编译会报错,当使用@class在两个类相互声明,就不会出现编译报错。


* 把其中的一个头文件中的import换成@class


* 面试题:#import和@class的区别。
* 作用上的区别
* import会包含引用类的所有信息(内容),包括引用类的变量和方法 @class仅仅是告诉编译器有这么一个类,具体这个类里有什么信息,完全不知道。
* 效率上的区别
* 如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,编译效率非常低相对来讲,使用@class方式就不会出现这种问题了。

##13、【理解】@class的使用* 场景![](http://img.blog.csdn.net/20161009214217550)####1.@class的使用* 作用 * 可以简单地引用一个类* 简单使用 * @class Dog; //类的引入 * 仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容* 具体使用 * 在.h文件中使用@class引用一个类 * 在.m文件中使用#import包含这个类的.h文件* 如下面代码:```objc  A.h文件 #import "B.h" @interface A : NSObject  {     B *b;  }  @end<div class="se-preview-section-delimiter"></div>
  • 为了简单起见:A类是引用类,B类是被引用类,这里先不考虑A类的实现文件。
  • 通常引用一个类有两种办法:

    • 一种是通过#import方式引入;
    • 另一种是通过@class引入;
  • 这两种的方式的区别在于:

    • 1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在 A.h文件中 B *b只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息;

    • 2)使用@class方式由于只需要知道被引用类(B类)的名称就可以了,而在实现类由于要用到被引用类中的实体变量和方法,所以需要使用#import来包含被引用类的头文件;

    • 3)通过上面2点也很容易知道在编译效率上,如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt(A->B, B->C,C->D…),一旦最开始的头文件稍有改动,后面引用到这个文件的所有类 都需要重新编译一遍,这样的效率也是可想而知的.而相对来讲,使用@class方式就不会出现这种问题了;

    • 所以:我们实际开发中尽量在.h头文件中使用@class

    • 4)对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类,B类的代码:
  • 当程序运行时,编译会报错,当使用@class在两个类相互声明,就不会出现编译报错。

  • 把其中的一个头文件中的import换成@class
  • 面试题:#import和@class的区别。
    • 作用上的区别
    • import会包含引用类的所有信息(内容),包括引用类的变量和方法 @class仅仅是告诉编译器有这么一个类,具体这个类里有什么信息,完全不知道。
    • 效率上的区别
    • 如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,编译效率非常低相对来讲,使用@class方式就不会出现这种问题了。

14、【理解】循环retain问题

循环retain的使用

  • 问题:人有一只狗,狗有一个主人。
class "Dog.h";@interface Person :NSObject@property(nonatomic,retain)Dog *dog;@end@class "Person.h";@interface Dog:NSObject@property(nonatomic,retain)Person *owner;@end//main.m//创建对象Dog *d = [Dog new];Person *p = [Person new];//循环引用p.dog = d;d.owner = p;//看似正确的释放代码[d release];[p release];<div class="se-preview-section-delimiter"></div>
  • 程序执行结果: p和d都没有被释放掉

  • 原理分析:
    14.1

p.dog  = d; //因为dog的set方法中是 进行了 [dog retain];dog2d.owner = p;//会让p的引用计数+1  owner2当执行了[d release];[p release];dog和owner 的引用计数变成1
  • 如图:
    14.1

  • 循环retain的场景

    • 比如A对象retain了B对象,B对象retain了A对象
    • 循环retain的弊端
    • 这样会导致A对象和B对象永远无法释放
  • 循环retain的解决方案

    • 当两端互相引用时,应该一端用retain、一端用assign
* 程序执行结果: p和d都没有被释放掉* 原理分析:![](http://img.blog.csdn.net/20161009215834728)```objcp.dog  = d; //因为dog的set方法中是 进行了 [dog retain];dog2d.owner = p;//会让p的引用计数+1  owner2当执行了[d release];[p release];dog和owner 的引用计数变成1
  • 如图:
  • 循环retain的场景

    • 比如A对象retain了B对象,B对象retain了A对象
    • 循环retain的弊端
    • 这样会导致A对象和B对象永远无法释放
  • 循环retain的解决方案

    • 当两端互相引用时,应该一端用retain、一端用assign
0 0