[Objective-C]第六天

本文详细介绍了MRC内存管理的原理、步骤、常见问题及解决方案,通过电商App案例演示了如何正确管理单个和多个对象的内存,包括野指针、僵尸对象和使用@property参数的最佳实践。

 前日内容复习:

1. Xcode4.4之前

     @property: 只生成getter、setter方法的声明.

     @synthesize: 生成私有属性.并生成getter、setter方法的实现.

     Xcode4.4开始.

     @property增强: 

     1). 自动生成私有属性.

     2). 自动生成私有属性的getter setter方法的声明和实现.

  2. 编译检查与运行检查

     编译检查: 

     通过1个指针去访问对象的成员的时候,编译判断可以不可以的准则: 指针的类型当中是否有这个成员.

     运行检查:

     在程序运行的时候,通过1个指针去访问对象的成员之前,还会检查指针所指向的对象中是否真的有这个成员.

  3. 万能指针(NSObject、id). instancetype

     NSObject和id 它们都是万能指针,可以指向任意的OC对象.

     NSObject指针访问对象的成员的时候,编译器会做编译检查.

     id指针调用指针指向的对象的方法的时候.编译器不做任何检查 直接通过.

     id指针的缺点: 不能使用->访问属性 也不能使用点语法.

     instancetype: 只能做为方法的返回值.代表返回当前类的对象.

  4. 动态类型检测 反射

     1). 判断指针指向的对象中 指定的方法是是否可以调用.

     2). 判断类方法是否可以调用.

     3). 判断对象是否为指定类的对象或者子类对象.

     4). 判断对象是否为指定类的对象.

     5). 判断指定的类是否为另外1个类的子类.

  5. 构造方法.

     1). new是1个类方法. 创建对象初始化对象返回对象.

     2). new方法的内部其实就是 alloc init

     3). init方法就叫做构造方法. 初始化对象

     4). 重写init构造方法.     

     5). 自定义构造方法.

    如果你想要调用init默认的构造方法.那么new可以. 也可以使用alloc init

    如果你像要调用自定义的构造方法.这个时候就必须先alloc 再调用自定义的init

02-内存管理概述

  1. 内存管理

     内存的作用:存储数据.

     1). 如何将数据存储到内存之中.

         声明1个变量.然后将数据存储进去.

     2). 当数据不再被使用的时候,占用的内存空间如何被释放.

  2. 内存中的五大区域

     : 局部变量. 当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收.

     : OC对象.使用C函数申请的空间.

     BSS段: 未初始化的全局变量、静态变量. 一旦初始化就回收 并转存到数据段之中.

     数据段: 已经初始化的全局变量、静态变量. 直到程序结束的时候才会被回收.

     代码段: 代码. 程序结束的时候,系统会自动回收存储在代码段中的数据.

     栈、BSS段、数据段、代码段存储在它们中的数据的回收,是由系统自动完成的.不需要我们干预.

  3. 分配在堆区中的OC对象,是肯定需要被回收的.

     iPhone 内存机制.

     40M 警告

     45M 警告

     120M 闪退.

     存储在堆中的OC对象,系统不会自动回收. 直到程序结束的时候才会被回收.

  4. 内存管理的范围:

     只需要管理存储在堆中的OC对象的回收.其他区域中的数据的回收是系统自动管理的.

  5. 对象应该什么时候被回收?

     当有人使用这个对象的时候,这个对象就千万不能回收.

     只有在没有任何人使用这个对象的时候,才可以回收.

  6. 引用计数器   

     1). 每1个对象都有1个属性.叫做retainCount.叫做引用计数器. 类型是unsigned long 占据8个字节.

         引用计数器的作用: 用来记录当前这个对象有多少个人在使用它.     

         默认情况下,创建1个对象出来 这个对象的引用计数器的默认值是1.

     2). 当多1个人使用这个对象的时候.应该先让这个对象的引用计数器的值+1 代表这个对象多1个人使用.

     3). 当这个对象少1个人使用的时候.应该先让这个对象的引用计数器的值-1 代表这个对象少1个人使用.

     4). 当这个对象的引用计数器变为0的时候.代表这个对象无人使用. 这个时候系统就会自动回收这个对象.

  7. 如何操作引用计数器.

     1). 为对象发送1条retain消息. 对象的引用计数器就会加1. 当多1个人使用对象的时候才发.

     2). 为对象发送1条release消息.对象的引用计数器就会减1. 当少1个人使用对象的时候才发.

     3). 为对象发送1条retainCount消息. 就可以去到对象的引用计数器的值.

     就这样++ -- 当对象的引用计数器变为0的时候,对象就会被系统立即回收.

     在对象被回收的时候.会自动调用对象的dealloc方法.

  8. 内存管理的分类

     MRC: Manual Reference Counting 手动引用计数.手动内存管理.

          当多1个人使用对象的时候,要求程序员手动的发送retain消息.少1个人使用的时候程序员手动的发送relase消息.

     2011年之前 iOS5之前

     ARC: Automatic Reference Counting  自动引用计数.自动内存管理.

     系统自动的在合适的地方发送retain relase消息.

     我们今天学习的MRC.

     学习MRC的理由:

     1). 面试必考 100%

     2). 早期的APP开发使用的MRC技术.

     3). iOS大牛都是从MRC成长起来的. 方便交流.

     4). ARC是基于MRC

03-第一个MRC程序

  1. iOS5开始. Xcode4.2开始就支持ARC

     Xcode7 默认支持ARC开发.

     默认使用的开发方式就是ARC的模式.

     关闭ARC开启MRC.

 默认是YES,改成NO。就是关闭ARC 开启MRC了。

  2. 当对象的引用计数器变为0的时候,系统会自动回收对象.

     在系统回收对象的时候.会自动的调用对象的dealloc方法.

     重写dealloc方法的规范:

     必须要调用父类的dealloc方法. 并且要放在最后一句代码.

-(void)dealloc
{
    NSLog(@"名字叫做%@的人挂了",_name);
    [super dealloc];
};

  3. 测试引用计数器.

     1). 新创建1个对象,这个对象的引用计数器的值默认是1.

     2). 当对象的引用计数器变为0的时候.对象就会被系统立即回收 并自动调用dealloc方法.

     3). 为对象发送retain消息 对象的引用计数器就会+1

  4. 为对象发送release消息.并不是回收对象.而是让对象的引用计数器-1

     当对象的引用计数器的值变为0的时候.对象才会被系统立即回收.

04-内存管理的原则

  1. 内存管理的重点

     1). 什么时候为对象发送retain消息.

         当多1个人使用这个对象的时候,应该先为这个对象发送retain消息.

     2). 什么时候为对象发送releaee消息.

         当少1个人使用这个对象的时候.应该为这个对象发送1条release消息.

  2. 在ARC机制下,retain release dealloc这些方法方法无法调用.

  3. 内存管理的原则

     1). 有对象的创建,就要匹配1个release

     2). retain的次数和release的次数要匹配.

     3). 谁用谁retain. 谁不用谁release.

         谁负责retain 谁就负责relase

     4). 只有在多1个人用的时候才retain 少1个人使用的时候才release

     有始有终,有加就有减. 有retain就应该匹配1个release 一定要平衡.

05-野指针与僵尸对象

  1. 野指针

     C语言中的野指针: 定义1个指针变量.没有初始化.这个指针变量的值是1个垃圾值,指向1块随机的空间.这个指针就叫做野指针.

     OC中的野指针: 指针指向的对象已经被回收了.这样的指针就叫做野指针.

 2. 对象回收的本质. 

     内存回收的本质: 

     申请1个变量,实际上就是向系统申请指定字节数的空间.这些空间系统就不会再分配给别人了.

     当变量被回收的时候,代表变量占用的字节空间从此以后系统可以分配给别人使用了.

     但是字节空间中存储的数据还在.

     回收对象:

     所谓的对象的回收,指的是对象占用的空间可以分配给别人.

     当这个对象占用的空间没有分配给别人之前 其实对象数据还在.

 3. 僵尸对象

    1个已经被释放的对象,但是这个对象所占的空间还没有分配给别人.这样的对象叫做僵尸对象.

    我们通过野指针去访问僵尸对象的时候.有可能没问题 也有可能有问题.

    当僵尸对象占用的空间还没有分配给别人的时候.这是可以的.

    当僵尸对象占用的空间分配给了别人使用的时候 就不可以.

 4. 我们认为只要对象称为了僵尸对象,无论如何 都不允许访问了.

    就希望如果访问的是僵尸对象,无论如何报错.

    僵尸对象的实时检查机制.可以将这个机制打开. 打开之后. 只要访问的是僵尸对象,无论空间是否分配 就会报错.

【实时检查对象机制的方法】

  选择[Set the active scheme]按钮菜单下的Edit Scheme

 

 或 选择菜单中的Product中的Scheme中的Edit Scheme.

 选择Diagnostics 选中 Enable Zombie Objects. 打开僵尸对象检查机制。

5. 为什么不默认打开僵尸对象检测.

    一旦打开僵尸对象检测 那么在每访问1个对象的时候 都会先检查这个对象是否为1个僵尸对象,

    这样是极其消耗性能的.【开发测试的时候可以打开,非开发测试不要打开

 6. 使用野指针访问僵尸对象会报错. 如何避免僵尸对象错误..

    当1个指针称为野指针以后.将这个指针的值设置nil

    当1个指针的值为nil 通过这个指针去调用对象的方法(包括使用点语法)的时候.不会报错. 只是没有任何反应.

     但是如果通过直接访问属性 -> 就会报错.

 7. 无法复活1个僵尸对象.

06-单个对象的内存管理

  1. 内存泄露.

      指的是1个对象没有被及时的回收.在该回收的时候而没有被回收

     一直驻留在内存中,直到程序结束的时候才回收.

  2. 单个对象的内存泄露的情况.   

     1). 有对象的创建,而没有对应的relase

     2). retain的次数和relase的次数不匹配.

     3). 在不适当的时候,为指针赋值为nil

     4). 在方法中为传入的对象进行不适当的retain

  3. 如何保证单个对象可以被回收

     1). 有对象的创建 就必须要匹配1个relase

     2). retain次数和release次数一定要匹配.

     3). 只有在指针称为野指针的时候才赋值为nil

     4). 在方法中不要随意的为传入的对象retain.

07-多个对象的内存管理

  用面向对象模拟1个案例:  

  凤姐开车去拉萨.

  人类:

    属性: 车.

    行为: 开车.

  车类:

    属性: 速度.

    行为: 行驶.

  1. 当属性是1个OC对象的时候. setter方法的写法.

     将传进来的对象赋值给当前对象的属性,代表传入的对象多了1个人使用,所以我们应该先为这个传入的对象发送1条retain消息 再赋值.

     当当前对象销毁的时候.代表属性指向的对象少1个人使用. 就应该在dealloc中relase

     代码写法:

     - (void)setCar:(Car *)car
     {
        _car = [car retain];
     }

     - (void)dealloc

     {
        [_car release];
        [super dealloc];
     }

  2. 当属性是1个OC对象的时候,setter方法照着上面那样写,其实还是有Bug的.

     当为对象的这个属性多次赋值的时候.就会发生内存泄露.

     发生泄露的原因: 当为属性赋值的时候, 代表旧对象少1个人用.新对象多1个人使用.

     应该relase旧的 retain新的.

     - (void)setCar:(Car *)car
     {
         [_car release];
         _car = [car retain];
     }
-(void)setCar:(Car*)car{

    [_car release]; //_car属性原本指向的对象少1个人使用.
    
    //将传入的车对象赋值给当前对象的_car属性。
    //代表:传入的对象多了1个人使用。
    //那么就应该先为这个对象发送1条retain消息
    
    //[car retain];//为传进来的对象发送1条retain消息,代表多1个人使用。
    //_car =[car retain];
    
    _car=[car retain];
    //为传进来的对象发送1条retain消息,让他的引用计数器的值+1 代表多1个人使用。
    //再将传入的对象赋值给当前对象的_car属性。
    //当我们将传入的Car对象赋值给_car属性的时候。
    //代表1:_car属性原本指向的对象少1个人使用.
    //代表2:传入的对象多1个使用.
    

}

   3. 出现的僵尸对象错误的原因:

      在于.新旧对象是同1个对象.

      解决的方案:  当发现新旧对象是同1个对象的时候.什么都不用做. 

                 只有当新旧对象不是同1个对象的时候 才release旧的 retain新的.

      最终完美版的setter方法的写法 应该这样写

- (void)setCar:(Car *)car
{
    if(_car != car)
    {
     [_car release];
     _car = [car retain];
    }
}

- (void)dealloc
{
    [_car releae];
    [super dealloc];
}

  4. 特别注意.

     我们每次管理的范围是 OC 对象.(NSString 也是OC对象)

     所以,只有属性的类型是OC对象的时候.这个属性的setter方法才要像上面那样写.

     如果属性不是OC对象类型的 setter方法直接赋值就可以了.

08-@property参数

  1. 在MRC的开发模式下.1个类的属性如果是1个OC对象类型的.

        那么这个属性的setter方法就应该按照下面的格式写.

      - (void)setCar:(Car *)car
     {
         if(_car != car)
         {
            [_car release];
            _car = [car retain];
         }
     }

     还要重写dealloc方法.

     - (void)dealloc
     {
        [_car release];
        [super delloc];
     }

     如果属性的类型不是OC对象类型的.不需要像上面那样写. 还是像之前那样写就OK了.

  2. @property

     1). 作用

         a. 自动生成私有属性.

         b. 自动生成这个属性的getter setter方法的声明.

         c. 自动生成这个属性的getter setter方法的实现.

         特别播报:

         生成的setter方法的实现中,无论是什么类型的,都是直接赋值.

  3. @property参数.

     1). @property可以带参数的.

         @property(参数1,参数2,参数3......)数据类型 名称;

     2). 介绍一下@property的四组参数.

         a. 与多线程相关的两个参数.

            atomic、nonatomic.

         b. 与生成的setter方法的实现相关的参数.

            assign、retain.

         c. 与生成只读、读写相关的参数       

            readonly readwrite

         d. 是与生成的getter setter方法名字相关的参数.

            getter  setter

  4. 介绍与多线程相关的参数.

     atomic: 默认值. 如果写atomic,这个时候生成的setter方法的代码就会被加上一把线程安全锁.

             特点: 安全、效率低下.

     nonatomic: 如果写nonatomic 这个时候生成的setter方法的代码就不会加线程安全锁.

             特点: 不安全,但是效率高.

     建议: 要效率. 选择使用nonatomic  在没有讲解多线程的知识以前 统统使用nonatomic

  5. 与生成的setter方法的实现相关的参数.

     assign: 默认值 生成的setter方法的实现就是直接赋值.

     retain: 生成的setter方法的实现就是标准的MRC内存管理代码.

            也就是. 先判断新旧对象是否为同1个对象 如果不是 release旧的   retain新的.

     当属性的类型是OC对象类型的时候,那么就使用retain

     当属性的类型是非OC对象的时候,使用assign.

     千万注意:

     retain参数.只是生成标准的setter方法为标准的MRC内存管理代码 不会自动的再dealloc中生成relase的代码.

     所以, 我们还要自己手动的在dealloc中release

  6. 与生成只读、读写的封装.

     readwrite: 默认值.代表同时生成getter setter

     readonly: 只会生成getter 不会生成setter

  7. 生成getter、setter方法名称相关的参数.

     默认情况下.@property生成的getter setter方法的名字都是最标准的名字.

     其实我们可以通过参数来指定@property生成的方法的名字.

     getter = getter方法名字 用来指定@property生成的getter方法的名字.

     setter = setter方法名字.用来指定@property生成的setter方法的名字. 注意.setter方法是带参数的 所以要加1个冒号. 

     记住:如果使用getter setter修改了生成的方法的名字.

          在使用点语法的时候.编译器会转换为调用修改后的名字的代码.

     修改生成的getter setter方法名字. 因为默认情况下生成的方法的名字已经是最标准的名字了.

     所以.一般情况下不要去改.

    1). 无论什么情况都不要改setter方法的名字. 因为默认情况下生成的名字就已经是最标准的了.

     2). 什么时候修改getter方法的名字.当属性的类型是1个BOOL类型的时候.就修改这个getter的名字以is开头 提高代码的阅读性.

 ------总结-------

 1. 与多线程相关的参数: 用nonatomic

 2. 与生成的setter方法实现相关的参数

    属性的类型是OC对象的时候 使用retain

    属性的类型是非OC对象的时候 使用assign

 3. 只读 读写.  

    如果你希望生成的封装是只读封装  那么就使用readonly

    如果希望读写封装 readwrite

 4.  1). 无论什么情况都不要改setter方法的名字. 因为默认情况下生成的名字就已经是最标准的了.

     2). 什么时候修改getter方法的名字.当属性的类型是1个BOOL类型的时候.就修改这个getter的名字以is开头 提高代码的阅读性.

 ------使用参数注意-----

 1. 同1组参数只能使用1个.

    getter setter可以同时使用.

 2. 参数的顺序可以随意.

09-@class

 1. 当两个类相互包含的时候. 当Person.h中包含Book.h 而Book.h中又包含Person.h

     这个时候,就会出现循环引用的问题. 就会造成无限递归的问题,而导致无法编译通过.

  2. 解决方案: 

     其中一边不要使用#import引入对方的头文件.

     而是使用@class 类名; 来标注这是1个类.这样子就可以在不引入对方头文件的情况下,告诉编译器这是1个类.

@class Car;

     在.m文件中再#import对方的头文件.就可以使用了.

  3. @class与#import的区别

     1). #import是将指定的文件的内容拷贝到写指令的地方.

     2). @class 并不会拷贝任何内容. 只是告诉编译器,这是1个类,这样编译器在编译的时候才可以知道这是1个类.

10-循环retain

  1. 当两个对象相互引用的时候.

     A对象的属性是B对象  B对象的属性是A对象.

     这个时候 如果两边都使用retain 那么就会发生内存泄露.

  2. 解决方案: 1端使用retain 另外1端使用assign 使用assign的那1端在dealloc中不再需要release了.


作业:

要求在MRC模式下写代码.

课上练习需求:
------------------------
练习需求:
电商App

要求:利用OC+面向对象设计下面的三个类:
一、商品类-Goods
	属性:
		商品名称
		单价
		重量
		商品展示图片
		生产日期(暂时用结构体表示)produceDate
		过期日期	expireDate


二、买家类(用户) Buyer
	属性:
		姓名
		性别(不要用 int,用枚举)
		年龄
		身高(单位:cm)
		
三、卖家类 - Seller
	属性:
		姓名
		性别(不要用int,用枚举)
		年龄
		身高(单位:cm)
		所出售商品(假设一个卖家就卖一件商品)
								
四、买家类、卖家类(抽象父类Person)

五、在main函数中创建卖家、商品、买家类的对象。


=================================作业==========================
设计一个微博app的部分类:

一、微博类 (Microblog)
	属性:
	* 文字内容
    * 图片
    * 发表时间 (可以用字符串表示NSString)
    * 作者
    * 被转发的微博
    * 评论数
    * 转发数
    * 点赞数

二、作者类 (User)
    * 名称
    * 生日
    * 账号
三、账号类 (Account)
    * 账号名称
    * 账号密码
    * 账号注册时间 (可以用字符串表示NSString)

// 在动手写之前,自我提问:这三个类应该先写哪一个?

 模拟场景:
    * 张三在2007-9-8 17:56:34的时候, 注册了一个账号(名称:itcast, 密码:123456)
    * 张三的生日是1998-7-4 18:46:24
    * 张三在2010-8-8 9:23:44的时候, 发布一条微博
         * 文字内容  @“今天心情不错”
         * 图片 @“goodDay.png”
         * 发表时间
         * 作者
         * 被转发的微博
         * 评论数 100
         * 转发数 290
         * 点赞数 2000
 
     * 李四在2006-9-8 19:26:54的时候, 注册了一个账号(名称:lisiitcast, 密码:654321)
     * 李四的生日是1999-9-6 14:16:28
     * 李四在2011-8-8 20:47:09的时候, 转发了张三之前发布的微博 ,并且还附带了一句话:@“今天心情确实不错”
     * 评论数 10
     * 转发数 20
     * 点赞数 200


//
//  date.h
//

#ifndef date_h
#define date_h
typedef struct {
    
    int year;
    int month;
    int day;
    
}date;

#endif /* date_h */
//
//  Goods.h
/*
 一、商品类-Goods
	属性:
     商品名称
     单价
     重量
     商品展示图片
     生产日期(暂时用结构体表示)produceDate
     过期日期	expireDate
*/

#import <Foundation/Foundation.h>
#import "date.h"


@interface Goods : NSObject

@property (nonatomic,retain) NSString * name;
@property (nonatomic,assign) float price;
@property (nonatomic,assign) float weight;
@property (nonatomic,retain) NSString * picLink;
@property (nonatomic,assign) date produceDate;
@property (nonatomic,assign) date expireDate;

@end
//
//  Goods.m
//

#import "Goods.h"
@implementation Goods
-(void)dealloc{
    NSLog(@"价值为%.2lf元的%@即将被销毁~",_price,_name);
    [_name release];
    [_picLink release];
    [super dealloc];
}
@end
//
//  Gender.h
//

#ifndef Gender_h
#define Gender_h
typedef enum {
    male = 1,
    female = 0,
}Gender;

#endif /* Gender_h */
//
//  Person.h
//
/*
 抽象父类:Person
     属性:
     姓名
     性别(不要用 int,用枚举)
     年龄
     身高(单位:cm)
 */

#import <Foundation/Foundation.h>
#import "Gender.h"

@interface Person : NSObject

@property (nonatomic,retain) NSString * name;
@property (nonatomic,assign) Gender gender;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) float height;


@end
//
//  Person.m
//
#import "Person.h"
@implementation Person
-(void)dealloc{
    NSLog(@"名字为%@的对象被销毁~",_name);
    [_name release];
    [super dealloc];
}
@end
//
//  Buyer.h
//
/*
 二、买家类(用户) Buyer 抽象父类Person
	属性:
 姓名
 性别(不要用 int,用枚举)
 年龄
 身高(单位:cm)
 */
#import "Person.h"

@interface Buyer : Person

@end
//
//  Buyer.m
//

#import "Buyer.h"

@implementation Buyer

@end
//
//  Seller.h
//
/*
 
 三、卖家类 - Seller 抽象父类Person
 属性:
     姓名
     性别(不要用int,用枚举)
     年龄
     身高(单位:cm)
    所出售商品(假设一个卖家就卖一件商品)
 */


#import "Person.h"
#import "Goods.h"
@interface Seller : Person

@property (nonatomic,retain) Goods * good;

@end
//
//  Seller.m
//


#import "Seller.h"

@implementation Seller

-(void)dealloc{
    
    [_good release];
    [super dealloc];
}

@end
//
//  main.m
//

#import <Foundation/Foundation.h>
#import "Buyer.h"
#import "Seller.h"

int main(int argc, const char * argv[]) {

    Goods * p1 = [Goods new];
    
    p1.name = @"王大锤的大铁锤";
    p1.price = 888.88;
    p1.weight = 99.99;
    p1.picLink = @"http://tiechui.wangdachui/pic.png";
    date dateProduce = {2011,11,11};
    date dateExpire = {2022,2,2};
    p1.produceDate = dateProduce;
    p1.expireDate = dateExpire;
    
    Seller * s1 = [Seller new];
    s1.name = @"京西";
    s1.gender = male;
    s1.age = 88;
    s1.height = 190;
    s1.good = p1;
    
    Buyer * b1 = [Buyer new];
    b1.name = @"逃包老妹";
    b1.gender = female;
    b1.age = 68;
    b1.height = 99;

    
    [b1 release];
    [s1 release];
    [p1 release];
    
    return 0;
}

//
//  Account.h
//

#import <Foundation/Foundation.h>

@interface Account : NSObject

@property (nonatomic,retain) NSString * accountId;
@property (nonatomic,retain) NSString * password;
@property (nonatomic,retain) NSString * regTime;

-(void)setAccountId:(NSString *)accountId andPassword:(NSString *)password andRegTime:(NSString *)regTime;

@end
//
//  Account.m
//

#import "Account.h"

@implementation Account

-(void)dealloc{
    [_accountId release];
    [_password release];
    [_regTime release];
    [super dealloc];
}

-(void)setAccountId:(NSString *)accountId andPassword:(NSString *)password andRegTime:(NSString *)regTime{
    
    _accountId = accountId;
    _password = password;
    _regTime =regTime;
    NSLog(@"%@ : 恭喜!【%@】注册成功!",_regTime,_accountId);
}

@end
//
//  User.h
//
/*
 二、作者类 (User)
 * 名称
 * 生日
 * 账号
 
 */
#import <Foundation/Foundation.h>
#import "Account.h"
@interface User : NSObject

@property (nonatomic,retain)NSString * name;
@property (nonatomic,retain)NSString * birthday;
@property (nonatomic,retain)Account * regInfo;

-(void)setName:(NSString *)name andBirthday:(NSString *)birthday andAccount:(Account *)regInfo;

@end
//
//  User.m
//

#import "User.h"

@implementation User

-(void)dealloc{
    NSLog(@"出生在%@的%@[%@]即将被销毁!",_birthday,_name,_regInfo.accountId);
//    [_regInfo release]; 已经在mian生成reginfo对象之后写了对应的销毁语句。
    [_birthday release];
    [_name release];
    [super dealloc];
}

-(void)setName:(NSString *)name andBirthday:(NSString *)birthday andAccount:(Account *)regInfo{
    _name = name;
    _birthday = birthday;
    _regInfo = regInfo;
}

@end
//
//  Microblog.h
//  day06
//
//  Created by babylon on 2022/5/15.
//  Copyright © 2022年 babylon. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "User.h"

@interface Microblog : NSObject

@property (nonatomic,retain)NSString * content;
@property (nonatomic,retain)NSString * picURL;
@property (nonatomic,retain)NSString * postTime;
@property (nonatomic,retain)User * user;
@property (nonatomic,retain)Microblog * microblog;
@property (nonatomic,assign)int review;
@property (nonatomic,assign)int forward;
@property (nonatomic,assign)int like;


@end
//
//  Microblog.m
//

#import "Microblog.h"

@implementation Microblog
-(void)dealloc{
    NSLog(@"微博内容为%@将被销毁.",_content);
    [_content release];
    [_picURL release];
    [_postTime release];
    [_user release];
    [_microblog release];
    [super dealloc];
}
@end
//
//  main.m
//

#import <Foundation/Foundation.h>
#import "Microblog.h"

int main(int argc, const char * argv[]) {

    
    Account * reg1 = [Account new];
    [reg1 setAccountId:@"itcast" andPassword:@"123456" andRegTime:@"2007-9-8 17:56:34"];
    
    User * u1 = [User new];
    [u1 setName:@"张三" andBirthday:@"1998-7-4 18:46:24" andAccount:reg1];
    
    Microblog * m1 = [Microblog new];
    
    m1.content =@"今天心情不错";
    m1.picURL = @"goodDay.png";
    m1.postTime = @"2010-8-8 9:23:44";
    m1.user = u1;
    m1.review = 100;
    m1.forward = 290;
    m1.like = 2000;
    
    
    
    Account * reg2 = [Account new];
    [reg2 setAccountId:@"lisiitcast" andPassword:@"654321" andRegTime:@"2006-9-8 19:26:54"];
    
    User * u2 = [User new];
    [u2 setName:@"李四" andBirthday:@"1999-9-6 14:16:28" andAccount:reg2];
    
    Microblog *m2 = [Microblog new];
    m2.content = @"今天心情确实不错";
    m2.postTime = @"2011-8-8 20:47:09";
    m2.user = u2;
    m2.review = 10;
    m2.forward = 20;
    m2.like = 200;
    
    
    [m2 release];
    [u2 release];
    [reg2 release];
    
    [m1 release];
    [u1 release];
    [reg1 release];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值