一、点语法的本质是方法的调用,而不是访问成员变量(OC中访问成员变量只能使用->),当使用点语法时编译器会自动展开成相应的方法(编译器特性)。
点语法陷阱(ARC):
1)死循环
- (void)setAge:(int)age {
self.age = age;
}
分析:self.age = age 实际为赋值调用 set 方法; 会被编译器展开成如下代码,造成了死循环!
- (void)setAge:(int)age {
[self setAge:age];
}
正确写法:
- (void)setAge:(int)age {
self->_age = age;
}
或:
- (void)setAge:(int)age {
_age = age;
}
说明:age为Person类的属性,在Person.m中我们可以使用 “->”访问私有成员变量 _age;
2)死循环
- (int)age {
return self.age;
}
此处的 self.age 实际为取值,调用 get 方法,造成死循环。展开如下:
- (int)age {
return [self age];
}
分析:此处的self.age,是取值,则会被编译器转为 [self age];即调用了get方法,造成了死循环!
正确写法:
- (int)age {
return self->_age;
}
或:
- (int)age {
return _age;
}
二、@property 和 @synthesize 关键字(编译器指令:告诉编译器要做什么)
先说一说为什么使用 @property
// .h
@interface Person : NSObject {
int _age;
}
- (void)setAge:(int)age;
- (int)age;
@end
// .m
@implementation Person
- (void)setAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}
@end
以上是手动实现的 age 属性的 set/get 方法。
@property 会告诉编译器,声明 age 属性的访问器(set/get)方法,免去我们手工书写的繁琐!
示例:
// .h 声明age属性
@property (nonatomic, assign) int age; // 等同于声明了age属性set和get方法
// 访问器
- (void)setAge:(int)age;
- (int)age;
@synthesize 会告诉编译器,生成 age 属性(set/get)的实现!
示例(通用写法):
// 告诉编译器生成 age 属性的访问器,且在访问器里访问 _age 成员变量;如果没有 _age 成员变量,则自动生成@private的_age成员变量(私有的)
@synthesize age = _age;
// @synthesize 等同于set和get的实现,书写在@implementation……@end 之间
- (void)setAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}
示例:
// .h
@property (nonatomic, assign) int age;
@synthesize age = _num;
分析:表示用 age 的 set 和 get 方法,修改 _num 属性变量的值
- (void)setAge:(int)age {
_num = age;
}
- (int)age {
return _num;
}
示例:
// 默认会访问 age 成员变量,而不是 _age;
@synthesize age;
三、Xcode4.4之后,
1)@property增强版。只要:
@property (nonatomic, assign) int age;
好处:系统就会帮我们实现 age 属性的 set/get 方法。
缺点:自动生成的成员变量 _age 是私有的,子类是不能访问的。
如果让子类访问父类的 _age 成员变量,须手动实现 _age 成员变量:
// .h
@interface Person : NSObject {
int _age;
}
@property (nonatomic, assign) int age;
@end
// Man 继承自 Person
@implementation Man
- (void)test {
// 父类Person,存在 _age 成员属性,则子类就可以使用 _age数形变量
NSLog(@"%d", _age);
}
@end
2)@property增强下重写 set/get 方法
1)如果手动实现了set方法,那么编译器就只生成get方法和对应的成员变量;
2)如果手动实现了get方法,那么编译器就只生成set方法和对应的成员变量;
3)如果set和get方法都是手动实现的,那么编译器将不会生成成员变量,且报错(Use of undeclared identifier ‘_age’)。
若需要重写 set/get 方法,正确写法:
@synthesize age = _age;
- (void)setAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}