一 init的疑惑
init是OC的构造方法,也即是初始化方法。init常见于创建对象实例,假如现有Person这个类,新建一个Person对象实例时:
Person *person = [ [Person alloc] init];
以上代码执行的时候,实际实现了以下三个过程:
- 向Person类发送了alloc消息,在堆内存中创建了该类的对象
- 对象收到init消息后,进行对象的初始化
- 返回对象中堆内存中的地址,赋值给person指针变量
1 未重写init方法
给对象分配内存空间后,需要初始化对象,才能使用该对象。但后来自己做了尝试,当给对象分配内存后,不初始化对象,还是可以操作对象中的实例变量和方法的:
// Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @property NSString *name; @property int age; /** 测试方法*/ - (void)testMethod; @end
// Person.m #import "Person.h" @implementation Person - (void)testMethod { NSLog(@"This is test method."); } @end
然后在main.m中创建对象来进行测试:
// main.m #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 为Person对象申请内存空间,但未初始化 Person *person = [Person alloc]; NSLog(@"\nperson: %@\nName: %@\nAge: %d", person, person.name, person.age); [person testMethod]; } return 0; }
输出结果:
2015-08-08 23:50:23.223 OC-类的练习01[484:9197] person: <Person: 0x100206730> Name: (null) Age: 0 2015-08-08 23:50:23.224 OC-类的练习01[484:9197] This is test method.
2 重写init方法
在以上代码的基础上,重写Person类的init方法:
// Person.m #import Person.h @implementation Person - (void)testMethod { NSLog(@"This is test method."); } - (instancetype)init { if (self = [super init]) { _age = 1; _name = @"Test"; } return self } @end
再在main.m中添加测试代码:
// main.m #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 为Person对象申请内存空间,但不初始化 Person *person = [Person alloc]; NSLog(@"\nperson: %@\nName: %@\nAge: %d", person, person.name, person.age); [person testMethod]; // 初始化对象 person = [person init]; NSLog(@"\nperson: %@\nName: %@\nAge: %d", person, person.name, person.age); } return 0; }
输出结果:
2015-08-09 09:55:19.059 OC-类的练习01[458:9440] person: <Person: 0x1001064f0> Name: (null) Age: 0 2015-08-09 09:55:19.060 OC-类的练习01[458:9440] This is test method. 2015-08-09 09:55:19.060 OC-类的练习01[458:9440] person: <Person: 0x1001064f0> Name: Test Age: 1
个人理解是,在向Person类发送alloc消息创建了对象后,内存已经将实例变量设为了最原始的值,比如这里的Age默认0,Name默认null,并可以操作其对象方法。而在未重写init方法之前发送init消息时,Age和Name的值保持跟未初始化时一致。在重写了init方法之后,再发送init消息时,则按照重写方法来设置属性的初始值。
不过很迷惑,这跟书上的说法不一致。《ios编程》原话:任何一个对象都必须在创建并且初始化后才能使用(page31)。在网上未能找到更详尽的解释,后续如有新的进展再做跟进。
二 定制自己的初始化方法
默认的init方法不一定适用每个场景,有时需要定制自己的初始化方法。继续以Person类作扩展,在Person.h中声明initWithName:(NSString *)name Age:(int)age方法:
// Person.h @interface Person : NSObject ... /** 定制自己的初始化方法*/ - (instancetype)initWithName:(NSString *)name Age:(int)age; @end
并在Person.m中添加以下代码:
// Person.m #import Person.h @implementation Person ... - (instancetype)initWithName:(NSString *)name Age:(int)age { if (self = [super init]) { _name = name; _age = age; } return self; } @end
在main.m中删除之前的测试代码,直接采用自定义的初始化方法来创建对象:
// main.m #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 采用自定义初始化方法创建对象 Person *person = [[Person alloc] initWithName:@"Tom Smith" Age:20]; NSLog(@"\nName: %@\nAge: %d", person.name, person.age); } return 0; }
输出结果:
2015-08-09 10:20:50.780 OC-类的练习01[471:13356] Name: Tom Smith Age: 20
三 重写description方法
description方法,其实类似于java中的toString方法。如果以“NSLog(@"%@", person);”的形式打印时,只会输出person对象的内存地址,重写该方法可以输出我们需要的信息:
// Person.m @implementation Person ... - (NSString *)description { return [[NSString alloc] initWithFormat:@"\nDescription\nName: %@\nAge: %d", _name, _age]; } @end
然后在main.m中直接打印:
// main.m #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 采用自定义初始化方法创建对象 Person *person = [[Person alloc] initWithName:@"Tom Smith" Age:20]; NSLog(@"%@", person); } return 0; }
输出结果:
2015-08-09 10:41:50.242 OC-类的练习01[494:17251] Description Name: Tom Smith Age: 20