黑马程序员-OC的构造方法init以及自定义构造方法

时间:2021-02-03 00:28:01
  • 之前我们利用类来创建对象用到了一个类方法new,可是new方法具体是怎么执行的呢?例如
1 Person *p = [Person new];

程序只要执行了这一行代码,内存中就会有一个Person类和Person对象,并且Person对象的所有实例变量都初始化为0,并且还有个isa指针指向Person类,那么这个对象实际上是通过两部产生的:第一步,调用Person类的alloc类方法分配一定的内存空间给Person对象,它的返回值就是对象本身,实际上也是这一段内存空间的首地址;第二步,调用对象init方法初始化对象的所有实例变量,并返回对象本身。由于OC本身的init方法很死板,他就是将所有实例变量初始化为0,但是用户往往是希望创建完了一个对象后,希望对象的某些实例变量有一个非0的初始值,所以重写init方法是很重要的。init方法的一般固定格式为

 1 - (id)init{
 2     // 调用父类的init方法将返回值赋值给当前对象
 3     self = [super init]; // 固定写法
 4     
 5     if( self != nil ){
 6         //需要自定义初始化的成员变量
 7     }
 8     // 对象初始化为我们想要的结果后,将对象本身返回
 9     return self;
10 }

当然上述代码也可以简化为:

1 - (id)init{
2     
3     if( self = [super init] ){
4         //需要自定义初始化的成员变量
5     }
6     // 对象初始化为我们想要的结果后,将对象本身返回
7     return self;
8 }

代码实现:

 1 #import <Foundation/Foundation.h>
 2 /*******Person类******/
 3 @interface Person : NSObject
 4 
 5 @property int age;
 6 
 7 - (id)init;
 8 
 9 @end
10 
11 @implementation Person
12 
13 - (id)init{
14     if ( self = [super init] ) {
15         _age = 1;
16     }
17     
18     return self;
19 }
20 
21 @end
22 
23 /*******Student类*******/
24 @interface Student : Person
25 
26 @property int score;
27 
28 - (id)init;
29 
30 @end
31 
32 @implementation Student
33 
34 - (id)init{
35     if ( self = [super init] ) {
36         _score = 60;
37     }
38     
39     return self;
40 }
41 
42 @end
43 
44 int main() {
45     // 创建对象并调用调用重写的init对象方法
46     Person *p = [[Person alloc] init];
47     Student *stu = [[Student alloc] init];
48     
49     // 测试init方法是否成功调用
50     NSLog(@"Person对象:年龄的初始值为%d", p.age);
51     NSLog(@"Student对象:年龄的初始值为%d,成绩的初始值为%d", p.age, stu.score);
52     return 0;
53 }
  • 但是这种重写init方法也有弊端,因为所有通过重写的init方法创建出来的对象的初始值都是一样的,即使他们都不为0,比如每个学生的姓名是不一样的,自然我们会想到自己写一个初始化方法来满足用户的要求,那么这种自定义的构造方法有什么规范呢?首先,方法名必须以init开头;其次因为是对象方法,所以是以减号'-'开头,并且它的返回值是oc对象,所以返回值为id类型;最后在方法里一定要调用父类对象方法[super init]

假如有一个Person类,它有实例变量_age(int型)和_name(NSString *型),如果我要想每创建一个Person对象出来后它的_age和_name都不一样,我们可以采取自定义构造方法

 1 /*
 2 定义一个Person类,实例变量_age(int类型)和_name(NSString类型)
 3  要求:自定义一个构造方法,对象被创建出来后,_name和_age都有一个非0值
 4  
 5  */
 6 #import <Foundation/Foundation.h>
 7 
 8 @interface Person : NSObject
 9 
10 @property int age;
11 @property NSString *name;
12 
13 - (id)initWithName:(NSString *)name andAge:(int)age;
14 
15 @end
16 
17 @implementation Person
18 
19 - (id)initWithName:(NSString *)name andAge:(int)age{
20     if ( self = [super init] ) {
21         _name = name;
22         _age = age;
23     }
24     
25     return self;
26 }
27 
28 @end
29 
30 int main() {
31     // 创建两个对象并调用调用自定义构造对象方法
32     Person *p1 = [[Person alloc] initWithName:@"ZhangSan" andAge:15];
33     
34     Person *p2= [[Person alloc] initWithName:@"LiSi" andAge:14];
35     
36     // 测试自定义构造方法
37     NSLog(@"第1个Person对象:姓名为%@,年龄为%d", p1.name, p1.age);
38     NSLog(@"第2个Person对象:姓名为%@,年龄为%d", p2.name, p2.age);
39     return 0;
40 }

 

程序正常运行

如果我们要求在这个基础上再添加一个Person类的子类Student并且新添加一个实例变量_no(int类型),并且每创建一个Student类对象_no都有一个自己的初始值。代码如下:

 1 #import <Foundation/Foundation.h>
 2 
 3 /**********Person类***********/
 4 @interface Person : NSObject
 5 
 6 @property int age; // 年龄
 7 @property NSString *name; // 姓名
 8 
 9 - (id)initWithName:(NSString *)name andAge:(int)age; // 自定义name和age的构造对象方法
10 
11 @end
12 
13 // Person类的实现
14 @implementation Person
15 // 自定义构造方法实现
16 - (id)initWithName:(NSString *)name andAge:(int)age{
17     if ( self = [super init] ) {
18         _name = name;
19         _age = age;
20     }
21     
22     return self;
23 }
24 
25 @end
26 /***********Student类***********/
27 @interface Student : Person
28 
29 @property int no; // 学号
30 // 学生的自定义构造方法
31 - (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no;
32 
33 @end
34 
35 // Student类的实现
36 @implementation Student
37 // 学生的自定义构造方法的实现
38 - (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no{
39     if ( self = [super init]) {
40         _no = no;
41         self.name = name;
42         self.age = age;
43     }
44     return self;
45 }
46 
47 @end
48 
49 int main() {
50     // 创建两个对象并调用调用自定义构造对象方法
51     Student *stu1 = [[Student alloc] initWithName:@"ZhangSan" andAge:14 andNo:8];
52     
53     Student *stu2= [[Student alloc] initWithName:@"LiSi" andAge:15 andNo:2];
54     
55     // 测试自定义构造方法
56     NSLog(@"第1个Student对象:姓名为%@,年龄为%d,学号为%d", stu1.name, stu1.age, stu1.no);
57     NSLog(@"第2个Student对象:姓名为%@,年龄为%d,学号为%d", stu2.name, stu2.age, stu2.no);
58     return 0;
59 }

但是这个代码中的第41、42行是在子类的对象方法中对父类的实例变量修改值,这是不建议的。假如以后我们修改了父类的某一个属性名的话,那么子类的这种语句就会出错。一般是谁的属性那就用谁的方法去修改它,所以我们要把父类的这两个属性调用父类的方法去修改它,如何完成这个要求呢?我们只需要动一动学生的自定义构造方法的实现:

1 // 修改后的学生的自定义构造方法的实现
2 - (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no{
3     if ( self = [super initWithName:name andAge:no]) {
4         _no = no;
5         //self.name = name;
6         //self.age = age;
7     }
8     return self;
9 }

这种做法就保证了子类的属性交给子类去操作,父类的属性交给父类去操作,互不干扰