合成存取方法
从Objective-C 2.0版本开始,它自动合成了setter方法和getter方法。
让系统自动合成setter和getter方法只要如下两步。
- 在类接口部分使用@property指令定义属性。
- 在类实现部分使用@synthesize指令声明该属性。
当采用上述两个步骤合成存取方法之后,不仅会合成成对的stter和getter方法,还会自动在类实现部分定义一个与getter方法同名的成员变量。
使用@synthesize property名 [= 成员变量名];
在上面的语法格式中,@synthesize后没有属性,如果没有指定成员变量名,成员变量默认与合成的getter方法同名。
从Xcode5开始, 编译器有了自动合成机制(Auto property synthesis),只写@property就可以自动生成_property成员变量和getter、setter方法的声明和实现, 不需要写synthesize了。为了方便理解我还会加上@synthesize
当使用@property定义property时,还可以在@property和类型之间用括号添加一些额外的指示符,可使用的特殊指示符如下。
- assign:该指定符指定对属性只是进行简单赋值,不更改对所赋的值的引用计数。
- atomic (nonatomic):指定合成的方法是否为原子操作。
- copy:如果使用copy指示符,当调用stter方法对成员变量赋值时,会将被赋值的对象复制一个副本,再将该副本值赋给成员变量。
copy释例:
@interface FKBooks : NSObject
@property (nonatomic) NSString* bookWithoutCopy;
@property (nonatomic, copy) NSString* bookWithCopy;
@end
@implementation FKBooks
@synthesize bookWithCopy;
@synthesize bookWithoutCopy;
@end
int main (void) {
@autoreleasepool {
FKBooks* books = [[FKBooks alloc] init];
NSMutableString* str1 = [NSMutableString stringWithString:@"《疯狂iOS讲义》"];
NSMutableString* str2 = [NSMutableString stringWithString:@"《疯狂jave讲义》"];
[books setBookWithCopy:str1];
[books setBookWithoutCopy:str2];
NSLog(@"bookWithCopy:%@\nbookWithoutCopy:%@", [books bookWithCopy], [books bookWithoutCopy]);
// 修改str1和str2字符串
[str1 appendString:@"好耶!"];
[str2 appendString:@"好耶!"];
// 该代码你会看到没有使用copy的bookWithoutCopy属性也被修改
NSLog(@"bookWithCopy:%@\nbookWithoutCopy:%@", [books bookWithCopy], [books bookWithoutCopy]);
}
}
由于books的bookWithoutCopy属性值与str2指向同一个NSMutableString对象,因此当程序修改str2指向的NSMutableString对象时,books的bookWithoutCopy属性也会随之改变。而books的bookWithCopy属性值指向的是str1指向的NSMutableString对象的一个副本,因此不会改变。
编译,运行程序可以看到如下输出:
2022-05-13 22:23:08.423063+0800 oc.programme[50284:1732789]
bookWithCopy:《疯狂iOS讲义》
bookWithoutCopy:《疯狂jave讲义》
2022-05-13 22:23:08.423259+0800 oc.programme[50284:1732789]
bookWithCopy:《疯狂iOS讲义》
bookWithoutCopy:《疯狂jave讲义》好耶!
Program ended with exit code: 0
使用点语法
前面介绍类通过@property、@synthesize合成setter和getter方法,每次设置和访问属性时都通过setter和getter方法完成。但实际上Objective-C允许使用简化的点语法访问属性和对属性赋值。
例:
#import <Foundation/Foundation.h>
@interface FKCard : NSObject
@property (nonatomic, copy) NSString* flower;
@property (nonatomic, copy) NSString* value;
@end
@implementation FKCard
@synthesize flower;
@synthesize value;
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
FKCard* card = [[FKCard alloc] init];
// 使用点语法设置属性
card.flower = @"♤";
card.value = @"A";
// 使用点语法访问属性
NSLog(@"我的扑克牌:%@ %@", card.flower, card.value);
}
}
输出为:
2022-05-14 21:18:13.273380+0800 oc.programme[51086:1769514] 我的扑克牌:♤ A
Program ended with exit code: 0
对象初始化
1. 为对象分配空间
创建对象,需要调用该类的alloc类方法来分配内存空间,实际上,这个alloc方法来自NSObject。
当程序通过某个类的方法时,系统帮我们完成如下事情。
(1)系统为该对象的所有实例变量分配内存空间。
(2)将每个实例变量的内存空间都重置为0。
2. 初始化方法与对象初始化
NSObject提供的init方法虽然可以完成初始化,但它完成的只是最基本的初始化。在实际编程中我们可以提供自己的init方法(实际就是重写NSObject的init方法),以加入任意的自定义处理代码对属性执行初始化。
例:
#import <Foundation/Foundation.h>
@interface FKUser : NSObject
@property NSString* name;
@property int age;
@end
@implementation FKUser
@synthesize name;
@synthesize age;
- (id) init {
// 调用父类的init方法,将初始化对象返回给self
// 如果self不为nil,则基本初始化成功
if (self = [super init]) {
// 执行初始化
self.name = @"小王";
self.age = 10;
}
return self;
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
// 创建对象并实现初始化
FKUser* user = [[FKUser alloc] init];
// 访问对象的成员变量
NSLog(@"名字:%@ 年龄:%d\n", user.name, user.age);
}
}
输出:
2022-05-14 22:00:23.502026+0800 oc.programme[51486:1786651] 名字:小王 年龄:10
Program ended with exit code: 0
3. 便利的初始化方法
前面的示例中只是重写了init方法,实际上,我们还可以为Objective-C提供更多便利的的初始化方法,这些初始化方法通常会以init开头,并允许自带一些参数。
例如下面定义FKCar接口:
@interface FKCar : NSObject
@property (nonatomic) NSString* brand;
@property (nonatomic) NSString* model;
@property (nonatomic) NSString* color;
- (id) initBrand:(NSString*) brand Model:(NSString*) model;
- (id) initBrand:(NSString*) brand Model:(NSString*) model Color:(NSString*) color;
@end
上面接口部分定义了两个自定义的initXxx方法,这两个initXxx方法可以根据参数执行自定义初始化。下面是该FKCar类实现部分:
@implementation FKCar
@synthesize brand;
@synthesize model;
@synthesize color;
- (id) init {
// 调用父类的init方法执行初始化,并将得到的对象赋值给self
if (self = [super init]) {
self.brand = @"奔驰";
self.model = @"01";
self.color = @"白色";
}
return self;
}
- (id) initBrand:(NSString*) brand Model:(NSString*) model {
// 调用本类的init方法执行初始化,并将得到的对象赋值给self
if (self = [self init]) {
self.brand = brand;
self.model = model;
}
return self;
}
- (id) initBrand:(NSString*) brand Model:(NSString*) model Color:(NSString*) color {
// 调用本类的initBrand: Model:方法执行初始化,并将得到的对象赋值给self
if (self = [self initBrand:brand Model:model]) {
self.color = color;
}
return self;
}
@end
如果系统中包含多个初始化方法,且初始化方法B中完全包含了初始化A,我们完全可以在初始化方法B中调用初始化方法A,这样就可以更好地进行代码复用。
下面是FKCar的测试代码:
int main(int argc, char * argv[]) {
@autoreleasepool {
FKCar *car1 = [[FKCar alloc] init];
FKCar *car2 = [[FKCar alloc] initBrand:@"宝马" Model:@"02"];
FKCar *car3 = [[FKCar alloc] initBrand:@"奥迪" Model:@"03" Color:@"Black"];
NSLog(@"Car1: %@ %@ %@", car1.brand, car1.model, car1.color);
NSLog(@"Car2: %@ %@ %@", car2.brand, car2.model, car2.color);
NSLog(@"Car3: %@ %@ %@", car3.brand, car3.model, car3.color);
}
}
上例中三个对象分别使用了三种初始化方法,下面是运行结果:
2022-05-15 14:30:00.788498+0800 oc.programme[52518:1841626] Car1: 奔驰 01 白色
2022-05-15 14:30:00.788678+0800 oc.programme[52518:1841626] Car2: 宝马 02 白色
2022-05-15 14:30:00.788697+0800 oc.programme[52518:1841626] Car3: 奥迪 03 Black
Program ended with exit code: 0