1 MRC练习

1.1 问题

引用计数是Objective-C语言采用的一种内存管理技术,当一个对象被创建在堆上后,该对象的引用计数就自动设置为1,如果在其它对象中的对象成员需要持有这个对象时,则该对象的引用计数被加上1,此时如果该对象被释放,内存管理程序将首先把该对象的引用计数减1,然后判断该对象的引用计数是否为0,由于其它对象在持有该对象时将引用计数加了1,所以此时该对象的引用计数减1后不为0,则内存管理程序将不会释放该对象。直到持有该对象的其它对象也被释放时,该对象的引用计数再次减1,变为0时,该对象在堆上所占的存储空间才被释放。

引用计数技术的使用能够实现对资源的自动管理。

iOS5.0开始引入自动引用计数技术,iOS7.0以后则默认使用自动引用计数技术。自动引用计数技术,简称为ARC,由于ARC的出现,相对以前的方式被称为手动引用计数技术,简称为MRC。

1.2 方案

本案例是强制使用MRC(手动引用计数技术)来管理对象的引用计数的一个练习。由于iOS7.0以后使用Xcode创建的工程默认使用ARC(自动引用计数技术),所以需要强制转换回MRC。转换的方法如下步骤:

首先,创建一个工程,然后选择“工程导航”中的“工程项”,如图-1所示:

图-1

然后在右边窗口中选择Build Settings,如图-2所示:

图-2

下一步选择All选项,如图-3所示:

图-3

最后,向下滚动屏幕,找到Apple LLVM 5.1 – Language – Objective C中的Objective-C Automatic Reference Counting,将右边的选项选择为NO。如图-4所示:

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义Integer类

首先,在Day03工程中新添加Integer.h文件,用于定义新的类Integer。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface Integer : NSObject
  3. @property int integer;
  4. -(void)print;
  5. @end

在上述代码中,为Integer类添加一个属性,整型变量integer;然后为Integer类添加一个方法print,用于将属性的值输出到控制台。

然后,在类Integer的实现部分,即在Integer.m文件中,添加print方法的实现。代码如下所示:

  1. #import "Integer.h"
  2. @implementation Integer
  3. -(void)print
  4. {
  5. NSLog(@"%d", self.integer);
  6. }
  7. @end

步骤二: 查看初始引用计数值

在Day03工程中新添加MRC.m文件,用于主程序,在主程序中定义Integer的对象并查看该对象的引用计数。此时对象的引用计数值为1。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Integer.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. // insert code here...
  7. Integer *int1 = [[Integer alloc] init];
  8. NSLog(@"%ld", [int1 retainCount]);
  9. }
  10. return 0;
  11. }

上述代码中,以下代码:

  1. NSLog(@"%ld", [int1 retainCount]);

retainCount消息将得到对象int1当前的引用计数值。

步骤三:增加引用

在主程序中添加另一个引用,再查看该对象的引用计数。此时对象的引用计数值不会发生变化,仍然为1。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Integer.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. // insert code here...
  7. Integer *int1 = [[Integer alloc] init];
  8. NSLog(@"%ld", [int1 retainCount]);
  9. Integer *int2 = int1;
  10. NSLog(@"%ld", [int1 retainCount]);
  11. }
  12. return 0;
  13. }

上述代码中,以下代码:

  1. Integer *int2 = int1;

仅仅是定义了一个指针,将其初始化为int1,这将不会改变对象int1的引用计数值。

步骤四:增加引用计数

由于此时有两个指针指向对象int1,int1的引用计数应该变为2。但在MRC下,要想将int1的引用计数值加1,必须手动添加retain消息才能实现。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Integer.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. // insert code here...
  7. Integer *int1 = [[Integer alloc] init];
  8. NSLog(@"%ld", [int1 retainCount]);
  9. Integer *int2 = int1;
  10. NSLog(@"%ld", [int1 retainCount]);
  11. [int2 retain];
  12. NSLog(@"%ld", [int1 retainCount]);
  13. }
  14. return 0;
  15. }

上述代码中,以下代码:

  1. [int2 retain];
  2. NSLog(@"%ld", [int1 retainCount]);

是向对象int2发送消息retain,该消息会使int2指向的对象的引用计数加1,由于int2与int1共同指向一个对象,所以向对象int1发送retainCount消息,得到的值为2。

步骤五:减少引用计数

在MRC下,要想将int1的引用计数值减1,必须手动添加release消息才能实现。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Integer.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. // insert code here...
  7. Integer *int1 = [[Integer alloc] init];
  8. NSLog(@"%ld", [int1 retainCount]);
  9. Integer *int2 = int1;
  10. NSLog(@"%ld", [int1 retainCount]);
  11. [int2 retain];
  12. NSLog(@"%ld", [int1 retainCount]);
  13. [int2 release];
  14. NSLog(@"%ld", [int1 retainCount]);
  15. }
  16. return 0;
  17. }

上述代码中,以下代码:

  1. [int2 release];
  2. NSLog(@"%ld", [int1 retainCount]);

是向对象int2发送消息release,该消息会使int2指向的对象的引用计数减1,由于int2与int1共同指向一个对象,所以向对象int1发送retainCount消息,得到的值为1。

步骤六:释放对象

在MRC下,如果int1的引用计数值减1后值为0,则内存管理程序将把int1所占的内存空间释放掉。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Integer.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. // insert code here...
  7. Integer *int1 = [[Integer alloc] init];
  8. NSLog(@"%ld", [int1 retainCount]);
  9. Integer *int2 = int1;
  10. NSLog(@"%ld", [int1 retainCount]);
  11. [int2 retain];
  12. NSLog(@"%ld", [int1 retainCount]);
  13. [int2 release];
  14. NSLog(@"%ld", [int1 retainCount]);
  15. [int1 release];
  16. NSLog(@"%ld", [int1 retainCount]);
  17. int1 = nil;
  18. [int1 print];
  19. }
  20. return 0;
  21. }

上述代码中,以下代码:

  1. [int1 release];
  2. NSLog(@"%ld", [int1 retainCount]);

是向对象int1发送消息release,该消息会使int1指向的对象的引用计数减1,由于int1指向的对象的引用计数已经为1,再减1,将会变为0。此时int1所指向的对象被释放了。但是,如果此时向对象int1发送retainCount消息,得到的int1的引用计数值仍然为1,这是因为此时retainCount消息访问的int1的地址空间,是已经释放的空间,在释放前该空间的引用计数变量没有清0的缘故。

1.4 完整代码

本案例中,类Integer声明,即Integer.h文件,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface Integer : NSObject
  3. @property int integer;
  4. -(void)print;
  5. @end

类Integer实现,即Integer.m文件,完整代码如下所示:

  1. #import "Integer.h"
  2. @implementation Integer
  3. -(void)print
  4. {
  5. NSLog(@"%d", self.integer);
  6. }
  7. @end

主程序,即MRC.m,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Integer.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. // insert code here...
  7. Integer *int1 = [[Integer alloc] init];
  8. NSLog(@"%ld", [int1 retainCount]);
  9. Integer *int2 = int1;
  10. NSLog(@"%ld", [int1 retainCount]);
  11. [int2 retain];
  12. NSLog(@"%ld", [int1 retainCount]);
  13. [int2 release];
  14. NSLog(@"%ld", [int1 retainCount]);
  15. [int1 release];
  16. NSLog(@"%ld", [int1 retainCount]);
  17. int1 = nil;
  18. [int1 print];
  19. }
  20. return 0;
  21. }

2 编写Student和Book类

2.1 问题

本案例需要创建一个Book类,类中有一个整型price属性,用于记录书的价格。还需要创建一个Student类,类中有两个带参属性,它们是整型的年龄age和类Book类型的book,分别用于存储学生的年龄和学生正在学习的书,并且有一个study方法,用于显示年龄为age的学生学习价格为book.price的书。

在主程序中创建Student类的一个对象和Book类的两个对象,首先让Student类的对象拥有一个Book类的对象,然后让Student类的对象更换了拥有对象,换成另一个Book类对象。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义类Book

首先,在Day03工程中新添加Book.h文件,用于定义新的类Book。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface Book : NSObject
  3. @property int price;
  4. @end

在上述代码中,定义了类Book,在类中有一个属性,是整型变量price,用于存储书的价格。

然后,在类Book的实现部分,即在Book.m文件中,添加dealloc方法,该方法在本类中实际上没有任何实际意义,只是在类的对象销毁时,在控制台上输出一个提示。

代码如下所示:

  1. #import "Book.h"
  2. @implementation Book
  3. -(void)dealloc{
  4. NSLog(@"书对象销毁了 price:%d",self.price);
  5. [super dealloc];
  6. }
  7. @end

dealloc方法不能在类外直接被调用,它是在一个对象被release并且对象的引用计数为0时,由内存管理程序调用的方法。

步骤二:定义类Student

首先,在Day03工程中新添加Student.h文件,用于定义新的类Student。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Book.h"
  3. @interface Student : NSObject
  4. {
  5. }
  6. @property(nonatomic,assign) int age;
  7. @property(nonatomic,retain) Book* book;
  8. -(void)study;
  9. @end

在上述代码中,定义了类Student,在类中有两个带参属性。

一个属性是整型变量age,如下代码所示:

  1. @property(nonatomic,assign) int age;

用于存储学生的年龄。它有两个参数,一个是nonatomic,它代表对属性赋值的时候不加锁,即在多线程环境下访问时可能会出现数据错误,如果需要在多线程环境下运行,为保证数据不会出现错误,可使用atomic参数,它会在对属性赋值的时候加锁。另一个参数是assign,对于C语言的基本数据类型,只能选取这个参数。

另一个属性是book,如下代码所示:

  1. @property(nonatomic,retain) Book* book;

用于存储学生正在学习的书。它有两个参数,一个是nonatomic,另一个参数是retain,该参数一般用于NSObject类及其子类的对象,这些对象在赋值时需要将引用计数加1,retain参数可以满足这样的需求。

然后,在类Student的实现部分,即在Student.m文件中,添加dealloc方法和study方法的实现。

代码如下所示:

  1. #import "Student.h"
  2. @implementation Student
  3. -(void)dealloc{
  4. //将所有引用类型的属性在此释放掉
  5. [self.book release];
  6. NSLog(@"学生对象销毁了,dealloc方法执行了");
  7. [super dealloc];
  8. }
  9. -(void)study{
  10. NSLog(@"学生 age:%d 学习书 price:%d 中的知识",self.age,self.book.price);
  11. }
  12. @end

dealloc方法在这个类中是必须有的,前面在讲到属性book的retain参数时,指出retain参数会将赋值给属性book的对象的引用计数加1,那么这个对象必须还要减1,其存储空间才能最终得到释放。但是只要Student类对象的存储空间没有释放,属性book的引用计数就不能减1,所以只有在Student类对象的存储空间释放时,通过dealloc将属性book的引用计数减1。

study方法要完成的工作是在控制台上输出学生的年龄和正在学习的书。

步骤三:在主程序中使用Student类和Book类

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Student.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. Student* stu1 = [[Student alloc]init];
  7. stu1.age = 18;
  8. Book* sanguo = [[Book alloc]init];
  9. sanguo.price = 10;
  10. stu1.book = sanguo;//将书对象给学生对象
  11. NSLog(@"%ld", [sanguo retainCount]);
  12. Book* hongloumeng = [[Book alloc]init];
  13. hongloumeng.price = 20;
  14. stu1.book = hongloumeng;
  15. NSLog(@"%ld", [hongloumeng retainCount]);
  16. NSLog(@"%ld", [sanguo retainCount]);
  17. [hongloumeng release];
  18. [sanguo release];
  19. [stu1 study];
  20. [stu1 release];
  21. }
  22. return 0;
  23. }

在上述代码中,以下代码:

  1. Student* stu1 = [[Student alloc]init];
  2. stu1.age = 18;

定义了一个Student类的对象stu1,并将stu1的age属性赋值为18。

在上述代码中,以下代码:

  1. Book* sanguo = [[Book alloc]init];
  2. sanguo.price = 10;
  3. stu1.book = sanguo;//将书对象给学生对象
  4. NSLog(@"%ld", [sanguo retainCount]);

首先,定义了一个Book类的对象sanguo,并将sanguo的price属性赋值为10。然后,将stu1的book属性赋值为sanguo。我们知道book属性有一个参数是retain,它会将赋值给它的对象的引用计数加1,那么现在将sanguo赋值给stu1的book属性,sanguo的引用计数会加1,所以当我们查看sanguo的引用计数值时会发现是2。

在上述代码中,以下代码:

  1. Book* hongloumeng = [[Book alloc]init];
  2. hongloumeng.price = 20;
  3. stu1.book = hongloumeng;
  4. NSLog(@"%ld", [hongloumeng retainCount]);
  5. NSLog(@"%ld", [sanguo retainCount]);

首先,定义了一个Book类的对象hongloumeng,并将hongloumeng的price属性赋值为20。然后,将stu1的book属性重新赋值为hongloumeng。我们知道book属性有一个参数是retain,它会将赋值给它的对象的引用计数加1,那么现在将hongloumeng赋值给stu1的book属性,hongloumeng的引用计数会加1,所以当我们查看hongloumeng的引用计数值时会发现是2。但如果此时再查看sanguo的引用计数值时会发现变成了1,这是为什么呢?原因是Student类的book属性参数retain,除了会将赋值给它的对象的引用计数加1外,在加1之前,还会将先前赋值给它的对象的引用计数减1。如本案例中,先将stu1的book属性赋值为sanguo,然后重新赋值为hongloumeng,那么在给hongloumeng引用计数加1之前,先将sanguo的引用计数减了一个1。

在上述代码中,以下代码:

  1. [hongloumeng release];
  2. [sanguo release];

对hongloumeng对象进行释放,它不会被从存储空间中删除,因为release只会将hongloumeng的引用计数由2减为1。但是,对sanguo对象进行释放,就会被从存储空间中删除,因为release只会将sanguo的引用计数由1减为0。

在上述代码中,以下代码:

  1. [stu1 study];

是向对象stu1发送study消息时,因为hongloumeng的引用计数为1,所以它能显示出hongloumeng的价格。

在上述代码中,以下代码:

  1. [stu1 release];

是释放对象stu1,在stu1的存储空间被释放之前,还会先调用Student类的dealloc方法,在dealloc方法中,hongloumeng的引用计数由1减为0,所以hongloumeng的对象存储空间被释放。而stu1的引用计数一直为1,此时对其进行release,将减为0,stu1的存储空间被释放。

2.3 完整代码

本案例中,类Book声明,即Book.h文件,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface Book : NSObject
  3. @property int price;
  4. @end

类Book实现,即Book.m文件,完整代码如下所示:

  1. #import "Book.h"
  2. @implementation Book
  3. -(void)dealloc{
  4. NSLog(@"书对象销毁了 price:%d",self.price);
  5. [super dealloc];
  6. }
  7. @end

本案例中,类Student声明,即Student.h文件,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Book.h"
  3. @interface Student : NSObject
  4. {
  5. }
  6. @property(nonatomic,assign) int age;
  7. @property(nonatomic,retain) Book* book;
  8. -(void)study;
  9. @end

类Student实现,即Student.m文件,完整代码如下所示:

  1. #import "Student.h"
  2. @implementation Student
  3. -(void)dealloc{
  4. //将所有引用类型的属性在此释放掉
  5. [self.book release];
  6. NSLog(@"学生对象销毁了,dealloc方法执行了");
  7. [super dealloc];
  8. }
  9. -(void)study{
  10. NSLog(@"学生 age:%d 学习书 price:%d 中的知识",self.age,self.book.price);
  11. }
  12. @end

主程序,即StudentBook.m,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Student.h"
  3. int main(int argc, const char * argv[])
  4. {
  5. @autoreleasepool {
  6. Student* stu1 = [[Student alloc]init];
  7. stu1.age = 18;
  8. Book* sanguo = [[Book alloc]init];
  9. sanguo.price = 10;
  10. stu1.book = sanguo;//将书对象给学生对象
  11. NSLog(@"%ld", [sanguo retainCount]);
  12. Book* hongloumeng = [[Book alloc]init];
  13. hongloumeng.price = 20;
  14. stu1.book = hongloumeng;
  15. NSLog(@"%ld", [hongloumeng retainCount]);
  16. NSLog(@"%ld", [sanguo retainCount]);
  17. [hongloumeng release];
  18. [sanguo release];
  19. [stu1 study];
  20. [stu1 release];
  21. }
  22. return 0;
  23. }

3 编写父类Animal和子类Cat、Dog

3.1 问题

本案例需要创建一个Animal类,类中有一个方法叫shout的方法,该方法默认输出 "动物会叫",这个类作为父类,派生出两个子类Cat类和Dog类。

Cat类没有覆盖父类的shout方法,而Dog覆盖了父类的shout方法,改成自己的输出,"汪汪汪"。

分别调用cat、dog对象的shout方法。

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义类Animal

首先,在Day03-2工程中新添加Animal.h文件,用于定义新的类Animal。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface Animal : NSObject
  3. @property int age;
  4. -(void)shout;
  5. @end

在上述代码中,定义了类Animal,在类中有一个属性,是整型变量age,用于存储动物的年龄。类中还有一个eat方法的声明。

然后,在类Animal的实现部分,即在Animal.m文件中,添加shout方法的实现,该方法在控制台上输出“动物会叫”。

代码如下所示:

  1. #import "Animal.h"
  2. @implementation Animal
  3. -(void)shout{
  4. NSLog(@"动物会叫");
  5. }
  6. @end

步骤二:定义类Cat

首先,在Day03-2工程中新添加Cat.h文件,用于定义新的类Cat。

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Animal.h"
  3. @interface Cat : Animal
  4. @end

在上述代码中,以下代码:

  1. @interface Cat : Animal

是定义一个类Cat,继承父类Animal。

然后,在类Cat的实现部分,即在Cat.m文件中,什么都不做。

代码如下所示:

  1. #import "Cat.h"
  2. @implementation Cat
  3. @end

步骤三:定义类Dog

首先,在Day03-2工程中新添加Dog.h文件,用于定义新的类Dog。

代码如下所示:

  1. #import "Animal.h"
  2. @interface Dog : Animal
  3. @end

上述代码中,类Dog继承与父类Animal。

然后,在类Dog的实现部分,即在Dog.m文件中,覆盖父类Animal中的shout方法。该方法在控制台上输出“汪汪汪”。

代码如下所示:

  1. #import "Dog.h"
  2. @implementation Dog
  3. -(void)shout
  4. {
  5. NSLog(@"汪汪汪");
  6. }
  7. @end

Dog类中覆盖了Animal类中的方法后, Dog类中有两个shout方法,一个继承自Animal类的shout方法,另一个是是自定义的shout方法。

步骤四:在主程序中使用Animal类、Cat类和Dog类

代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Dog.h"
  3. #import "Cat.h"
  4. int main(int argc, const char * argv[])
  5. {
  6. @autoreleasepool {
  7. Animal* animal = [[Animal alloc]init];
  8. animal.age = 30;
  9. //Cat继承于Animal
  10. Cat* cat = [[Cat alloc]init];
  11. NSLog(@"cat age:%d",cat.age);
  12. [cat shout];
  13. //Dog继承于Animal
  14. Dog* dog = [[Dog alloc]init];
  15. dog.age = 18;//可以得到父类属性
  16. NSLog(@"dog age:%d",dog.age);
  17. [dog shout];
  18. }
  19. return 0;
  20. }

在上述代码中,以下代码:

  1. Animal* animal = [[Animal alloc]init];
  2. animal.age = 30;

定义了一个Animal类的对象animal,并将animal的age属性赋值为30。

在上述代码中,以下代码:

  1. //Cat继承于Animal
  2. Cat* cat = [[Cat alloc]init];

定义了一个Cat类的对象cat。

在上述代码中,以下代码:

  1. NSLog(@"cat age:%d",cat.age);

由于定义Cat类的对象cat后,没有为从Animal类继承过来的属性age赋值,所以输出的猫的年龄为0。

在上述代码中,以下代码:

  1. [cat shout];

对cat对象发送的shout消息,调用的是父类Animal中shout。因为定义Cat类时没有覆盖父类Animal类中的方法shout,所以在Cat类中只有从父类Cat中继承的shout方法。

在上述代码中,以下代码:

  1. //Dog继承于Animal
  2. Dog* dog = [[Dog alloc]init];
  3. dog.age = 18;//可以得到父类属性

定义了一个Dog类的对象dog,并对从Animal类继承过来的属性age赋值为18。

在上述代码中,以下代码:

  1. NSLog(@"dog age:%d",dog.age);

由于定义Dog类的对象dog后,已经为从Animal类继承过来的属性age赋值为18,所以输出的狗的年龄为18。

在上述代码中,以下代码:

  1. [dog shout];

对dog对象发送的shout消息,调用的是Dog类中shout。因为定义Dog类时已经覆盖父类Animal类中的方法shout,即在Dog类中有两个shout方法,一个是从父类Animal中继承的shout方法,另一个是自定义的shout。按照继承的语法规则,当向Dog类的对象dog发送shout消息时,优先调用自定义的shout方法。

3.3 完整代码

本案例中,类Animal声明,即Animal.h文件,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface Animal : NSObject
  3. @property int age;
  4. -(void)shout;
  5. @end

类Animal实现,即Animal.m文件,完整代码如下所示:

  1. #import "Animal.h"
  2. @implementation Animal
  3. -(void)shout{
  4. NSLog(@"动物会叫");
  5. }
  6. @end

本案例中,类Cat声明,即Cat.h文件,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Animal.h"
  3. @interface Cat : Animal
  4. @end

类Cat实现,即Cat.m文件,完整代码如下所示:

  1. #import "Cat.h"
  2. @implementation Cat
  3. @end

本案例中,类Dog声明,即Dog.h文件,完整代码如下所示:

  1. #import "Animal.h"
  2. @interface Dog : Animal
  3. @end

类Dog实现,即Dog.m文件,完整代码如下所示:

  1. #import "Dog.h"
  2. @implementation Dog
  3. -(void)shout
  4. {
  5. NSLog(@"汪汪汪");
  6. }
  7. @end

主程序,即AnimalCatDog.m,完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. #import "Dog.h"
  3. #import "Cat.h"
  4. int main(int argc, const char * argv[])
  5. {
  6. @autoreleasepool {
  7. Animal* animal = [[Animal alloc]init];
  8. animal.age = 30;
  9. //Cat继承于Animal
  10. Cat* cat = [[Cat alloc]init];
  11. NSLog(@"cat age:%d",cat.age);
  12. [cat shout];
  13. //Dog继承于Animal
  14. Dog* dog = [[Dog alloc]init];
  15. dog.age = 18;//可以得到父类属性
  16. NSLog(@"dog age:%d",dog.age);
  17. [dog shout];
  18. }
  19. return 0;
  20. }
posted on 2015-12-15 19:39  A蜗牛为梦想而生A  阅读(292)  评论(0编辑  收藏  举报