知识点安插:如果访问权限允许,objective-c 允许直接通过对象来访问成员变量。语法格式:对象->成员变量名;
对象与指针
这里沿用上篇objective-c面向对象之——类和对象(上)的代码。
在 FKPersonTest.m中,有这样的代码:
FKPerson* person = [[FKPerson alloc] init];
这行代码产生了2个东西,一个是 person 变量,一个是 FKPerson 对象。这个FKPerson 对象被赋给person 变量。
从类的定义来看,FKPerson对象包含3个成员变量(2个暴露,1个隐藏),成员变量是需要内存来存储。因此,创建FKPerson对象时,必须有对应的内存来存储FKPerson对象的成员变量。FKPerson对象在内存里的存储示意图:
有图可以看出,FKPerson对象由多块内存组成,不同的内存块存储了不同成员变量。当把FKPerson对象赋值给FKPerson*
变量时,其实是把FKPerson对象在内存中的首地址赋给了FKPerson*
变量。FKPerson*类型的变量指向实际的对象。
本质上说,类是一种指针类型的变量。因此,程序定义的FKPerson*类型只是存放一个地址值,它被保存在该 main()) 函数的动态存储区,它指向实际的FKPerson对象,而真正的FKPerson对象存放在堆(heap) 内存里。如下示意图:
当一个对象被成功创建后,这个对象将保存在堆内存中,objective-c 不允许直接访问堆内存中的对象,只能通过该对象的指针变量来访问对象。就是说,所有的对象都只能通过指针变量来访问它们。堆内存里的对象可以有多个指针,即多个指针变量可以指向同一个变量。
如果堆内存里的对象没有任何变量指向该对象,那么程序将无法再访问该对象,如果程序员不释放该对象所占用的内存,就会造成内存泄露。
self 关键字
self 关键字总是指向调用该方法的对象。 它的最大作用是:让类中的一个方法访问该类的另一个方法或成员变量。
self 总是代表当前类的对象,当 self 出现在某个方法体中时,它所代表的对象是不确定的。但它的类型是确定的:它代表的对象只能是当前类的示例。当这个方法被调用时,它代表的对象才能确定下来:谁调用该方法,self 就代表谁。
示例代码:
头文件:FKDog.h
#import <Foundation/Foundation.h>
@interface FKDog : NSObject
// 定义一个jump方法
- (void) jump;
// 定义一个run方法,run方法需要借助jump方法
- (void) run;
@end
实现文件:FKDog.m
#import "FKDog.h"
@implementation FKDog
// 实现一个jump方法
- (void) jump
{
NSLog(@"正在执行jump方法");
}
// 实现一个run方法,run方法需要借助jump方法
- (void) run
{
// FKDog* d = [[FKDog alloc] init];
// [d jump];
[self jump];
NSLog(@"正在执行run方法");
}
@end
FKDogTest.m文件:
#import <Foundation/Foundation.h>
#import "FKDog.h"
int main(int argc , char * argv[])
{
@autoreleasepool{
// 创建Dog对象
FKDog* dog = [[FKDog alloc] init];
// 调用Dog对象的run方法
[dog run];
}
}
用self区分重名的局部变量和成员变量
局部变量和成员变量重名的情况下,局部变量会隐藏成员变量。为了在方法中强行引用成员变量,可以使用 self 关键字进行区分。
示例代码:
FKWolf.h文件
#import <Foundation/Foundation.h>
@interface FKWolf : NSObject
{
NSString* _name;
int _age;
}
// 定义一个setName:ageAge方法
- (void) setName: (NSString*) _name andAge: (int) _age;
// 定义一个info方法
- (void) info;
@end
FKWolf.m 文件
#import "FKWolf.h"
@implementation FKWolf
// 定义一个setName:ageAge方法
- (void) setName: (NSString*) _name andAge: (int) _age
{
// 当局部变量隐藏成员变量时,
// 可用self代表调用该方法的对象,这样即可为调用该方法的成员变量赋值了。
self->_name = _name;
self->_age = _age;
}
// 定义一个info方法
- (void) info
{
NSLog(@"我的名字是%@, 年龄是%d岁" , _name , _age);
}
@end
int main(int argc , char * argv[])
{
@autoreleasepool{
FKWolf* w = [[FKWolf alloc] init];
[w setName: @"灰太狼" andAge:8];
[w info];
}
}
把 self 当成普通方法的返回值
当 self 作为对象的默认引用使用时,程序可以像访问普通指针变量一样访问这个 self引用,甚至可以把 self 当成普通方法的返回值。
示例程序:
ReturnSel.m文件
#import <Foundation/Foundation.h>
@interface ReturnSelf : NSObject
{
@public
int _age;
}
- (ReturnSelf*) grow;
@end
@implementation ReturnSelf
- (ReturnSelf*) grow
{
_age++;
// return self,返回调用该方法的对象
return self;
}
@end
int main(int argc , char * argv[])
{
@autoreleasepool{
ReturnSelf* rt = [[ReturnSelf alloc] init];
//可以连续调用同一个方法
[[[rt grow] grow] grow];
NSLog(@"rt的_age成员变量的值是:%d" , rt->_age);
}
}
说明:使用 self 作为方法的返回值可以使代码更加简洁,但可能造成实际意义的模糊。
id 类型
id 类型可以代表所有对象的类型。任意类的对象都可赋值给 id 类型的变量。
通过 id 类型的变量来调用方法时,objective-c 将会执行动态绑定。动态绑定:objective-c 将会跟踪对象所属的类,会在运行时判断该对象所属的类,并在运行时确定需要动态调用的方法,而不是在编译时确定要调用的方法。
示例程序:
#import <Foundation/Foundation.h>
#import "FKPerson.h"
int main(int argc , char * argv[])
{
@autoreleasepool{
// 定义id类型的变量,并将FKPerson对象赋给该变量
id p = [[FKPerson alloc] init];
// 使用p变量来调用say:方法。
// 程序将在运行时执行动态绑定,因此实际执行FKPerson对象的say:方法
[p say: @"你好,疯狂iOS讲义"];
}
}