实例变量 、 属性 、便利构造器、设置器、 访问器、实例方法("-") 、类方法("+"静态方法)、单例
类和对象
可以认为方法是面相过程的,而实例就是面相对象的。
实例变量
概述:
实例变量又叫成员变量,它有三种访问修饰符@private,@protected,@private,默认是@protected,在子类的实现文件中不能直接调用父类私有的变量(要用setter和getter方法才能访问到其父类私有的成员变量,这体现了面相对象的封装性);实例变量命名都用下划线“_”与系统命名保持一致
调用方式:
@public 直接使用‘->’
@private @protected都需要分别给出设置方法和访问方法
#import <Foundation/Foundation.h>
/*
@abstrcut 学生实体类
@author Elena
*/
@interface Student : NSObject
{
int _age; //实例变量
NSString *_name; //实例变量
}
属性
概述:
属性可以认为是对象(或类)的特征,他表示了对象的当前状态,它是供外界访问和设置用的。
property 属性是一组设置器和访问器,需要声明和实现,通过声明属性来摆脱繁琐的setter和getter方法,也就是说写了属性之后我们可以不必再写getter和setter方法了(它会通知编译器在内部就自动生成了setter和getter方法了,因此它们在某种程度上是等价的,),但是属性自动生成的setter和getter方法只存在简单的赋值,没有逻辑判断。
手动设置setter和getter方法的好处能进行正确性校验,业务逻辑判断。name = _name,_name是对外的也就是属性,name是对内的也就使成员变量。
主张使用@property这样,这样不用在写setter和getter方法,在property中使用了retain那么在相应的dealloc方法中要将该成员变量release一次。特别注意:若属性是对象类型的那么必须使用retain
示例代码
//默认的setter方法
-(void)setName:(NSString *)aName
{
if (_name != aName)
{
[_name release];
_name = [aName retain]; //这句话等价与 [aName rtain]; _name = aName;
}
}
//默认的getter方法
-(NSString *)aName
{
return _name;
}
#import "Student.h"
@implementation Student
@synthesize age = _age,name=_name;
@end
便利构造器
便利构造器是一中特殊的方法,能快速的构造出一个对象并对其初始化。这个初始化的临时对象不用手动释放,类方法的便利构造器初始化实例变量后不用手动release,然后直接用stu来调用类的方法来使用被初始化的值
//便利构造器
//示例方法
-(Student *)initWithName:(NSString *)name
andAge:(int)age
andHobby:(NSString *)hobby
andAddress:(NSString *)address;
//类方法
+(Student *)studentWithName:(NSString *)name
andAge:(int)age
andHobby:(NSString *)hobby
andAddress:(NSString *)address;设置器setter
格式:set+首字母大写的实例变量名
声明部分(.h)
-(void)setName:(NSString *)name;
-(NSString *)name;
实现部分(.m)
-(void)setName:(NSString *)name
{
self.name=name;
}
-(NSString *)name
{
return _name;
}与实例变量名相同(没有短横线),并且返回值类型也一致
方法
方法是类的行为,写在接口(.h)和实现(.m)两个文件中。在接口部分声明方法,在实现部分实现方法。
实例方法(“-”方法)
实例方法依赖于具体的对象,它必须通过对象来调用。
实例方法的调用
首先需要实例化该类 例如:Student *stu = [Student alloc] init];
[类的实例 实例方法名称] 例如:[stu sayHello];
注意:此处实例化该类时,调用了该类的构造函数init,并且该类的构造函数调用[super init]的返回值不等于该类的self。
定义子类的实例
Woman *wife = [Woman alloc] init];
此处实例化该类时,调用了该类的构造函数init,并且该类的构造函数调用[super init]的返回值 等于该类的self。
类方法(“+”方法)
类调用方法,不依赖于任何对象的方法,类似于c语言的static关键字 static函数。主要用于用在便利构造器和单例中
归纳小结
类方法的调用 [类名称 类方法名称];
这里需要注意:
1、类方法可以调用类方法。
2、类方法不可以调用实例方法,但是类方法可以通过创建对象来访问实例方法。
3、类方法不可以使用实例变量。类方法可以使用self,因为self不是实例变量。
4、类方法作为消息,可以被发送到类或者对象里面去(实际上,就是可以通过类或者对象调用类方法的意思)。
5、使用“+”方法,必须要初始化一个减号方法。
6、只在.m中实现,不在.h中声明,此时该方法或变量为私有的,不能被外部类所访问 ,此时在.m文件中会有一个警告。
7、调用方便。
内存中的静态区
静态去中的变量是不需要释放的,静态变量只初始化一次
+(id)getInstance
{
static id teacher = nil;
if (!teacher) {
teacher = [[self alloc]init];
}
return teacher;
}
属性的属性
属性也可以设置属性(attribute):读写(readonly,默认是readwrite)属性,原子性(atomic)属性,非原子性(nonatomic,这是不安全的)setter语义属性
(1)readonly 只读 ,也就是外部只能读取但是不能修改该属性
(2)给setter和getter方法起别名(setter = a:, getter = b)
atomic 开启多线程变量保护,会消耗一定的资源(非原子性,保证多线程安全)
nonatomic:禁止多线程变量保护,提高性能
(3)setter语义属性:
assign:直接赋值,适用于基本数据类型(非对象类型)
retain:赋值时做内存优化,使用于对象类型
copy:复制一个副本,适用于特殊的对象类型(有NSCoping协议的才可以用copy)
assign retain copy的setter方法的内部实现(笔试题)
assign:
@property float price;
内部实现:
- (void)setPrice:(float)price
{
_price = price;
}
getter是:
- (float)price
{
return _price;
}
retain:
@property (retain, readwrite, nonatomic) NSString *company;
内部实现:
- (void)setCompany:(NSString *)company{
if(_company != company){
[_company release];
[company retain];
_company = company;
}
}
copy:
@property (copy, readwrite, nonatomic) NSString *company;
内部实现:
- (void) setCompany:(NSString *)company{
if(_company != company){
[_company release];
[company copy];
_company = company;
}
}
三、使用属性和点语法
点语法(和[receriver message]是等价的)
1.性能有点差,内部转化为setter,getter
2.不易理解苹果的调用机制
3.属性
只要有setter(或getter)就可以使用点语法
四、封装
封装的好处:
使用起来更加简单
变量更加安全
可以隐藏内部实现细节
开发速度加快
http://item.taobao.com/item.htm?spm=a1z10.1.w5267445-2480627169.23.dbJJxG&id=20257771217
另外,总结一下:
1. 静态变量位于方法区,生命周期取决于类何时被加载及卸载。
2. 实例变量位于堆区,生命周期取决于实例何时被创建及销毁。
3. 局部变量位于栈区,生命周期取决于所属的方法合适被调用及结束调用。
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。