- 之前我们利用类来创建对象用到了一个类方法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 }
这种做法就保证了子类的属性交给子类去操作,父类的属性交给父类去操作,互不干扰