【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态

一. Objective-C 方法详解

1. 方法属性

(1) OC 方法传参机制

Object-C 方法传参机制 : OC 中得参数传递都是值传递, 传入参数的是参数的副本;

-- 基本类型 (值传递) : int 等基本类型直接传入 这些基本类型的的副本;

-- 指针类型 (地址传递) : 使用指针变量作为参数, 传递的也是指针变量的副本, 但是这个副本本身的值是一个地址, 地址 变量 和 地址 变量的副本 还是指向同一个地址;

(2) OC 方法 与 传统函数

方法 与 传统函数 :

-- 结构化编程语言 : 整个软件由一个函数构成, 如 C 语言, 一个 main 函数就是整个软件架构;

-- 面向对象语言 : 整个软件由 类 组成, 软件中的 方法 必须属于类, 不能独立存在;

(3) 类方法 与 实例方法

方法定义 : 方法只能定义在类中, 不能独立定义;

-- 类方法 : 使用 "+" 标识, 这个方法属于类方法, 使用 [类 方法] 调用;

-- 实例方法 : 使用 "-" 标识, 该方法属于实例方法, 使用 [对象 方法] 调用;

(4) 方法属性

方法属性 :

-- 不独立存在 : 不能独立定义, 只能在类中定义;

-- 类 对象 : 方法要么属于类, 要么属于对象;

-- 方法执行 : 方法不能独立执行, 需要使用 类 或 对象作为调用者;

2. 形参个数可变的方法

形参可变方法简介 :

-- 方法定义 : 最后一个形参后增加 逗号 和 三点 ", ..." ;

-- 示例 : NSLog() 函数可以传入任意多参数, 该函数就是形参个数可变的方法;

示例代码 :

-- 接口 :

/*************************************************************************
    > File Name: Varargs.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日  9/27 22:04:34 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@interface Varargs : NSObject

/*
 * 形参个数可变方法
 * 在 NSString * 类型参数后面加上 ",..." 一个逗号 三个点号,
 * 表明该方法还可以接受 n 个 NSString * 类型的参数
 */
-(void) varargs_test : (NSString *) str, ...;

@end

-- 实现 :

/*************************************************************************
    > File Name: Varargs.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日  9/27 22:24:55 2015
 ************************************************************************/

#import "Varargs.h"

@implementation Varargs

-(void) varargs_test : (NSString *) str, ...
{
	/*
	 * va_list 类型 : 用于指向可变参数列表的指针变量
	 */
	va_list argList;
	// str 如果为 null 的话, 地址就会为0
	if(str)
	{
		NSLog(@"%@", str);
		/*
		 * 开始处理可变形参的列表,
		 * 并让指针变量指向可变形参列表的第一个参数
		 * 开始提取可变参数列表的参数
		 */
		va_start(argList, str);
		/*
		 * va_arg : 提取指针当前指向的参数,
		 * 并将指针移动到下一个参数
		 */
		NSString * arg = va_arg(argList, id);
		//如果指针指向的参数 不为 nil , 则进入循环体
		while(arg)
		{
			NSLog(@"%@", arg);
			arg = va_arg(argList, id);
		}
		/*
		 * var_end : 结束处理可变形参, 释放指针变量
		 */
		va_end(argList);
	}
}

@end

-- main 函数 :

/*************************************************************************
    > File Name: VarargsTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日  9/27 22:33:09 2015
 ************************************************************************/

#import "Varargs.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		Varargs * va = [[Varargs alloc]init];
		[va varargs_test : @"参数一", @"参数二", @"参数三", @"参数四", nil];
	}
}

-- 执行过程 : 执行 clang -fobjc-arc -framework Foundation Varargs.m VarargsTest.m 命令;

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation Varargs.m VarargsTest.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 08:58:55.177 a.out[2823:507] 参数一
2015-09-30 08:58:55.180 a.out[2823:507] 参数二
2015-09-30 08:58:55.180 a.out[2823:507] 参数三
2015-09-30 08:58:55.181 a.out[2823:507] 参数四
octopus-2:oc_object octopus$ 

二. Object-C 成员变量

1. 成员变量机制

(1) 变量分类

变量分类 : 成员变量, 局部变量, 全局变量;

-- 局部变量 : 在函数里面定义的变量;

-- 全局变量 : 在函数外定义的变量;

-- 成员变量 : 在下面讲解;

(2) 成员变量简介

成员变量 : 在类 接口 或  实现 部分定义的变量, 都是实例变量, 不支持真正的类变量;

-- 实例变量 : 类对象被创建开始存在, 系统销毁对象, 实例变量就会随之销毁;

-- 实例变量访问 : "实例->实例变量";

(3) 成员变量 源码示例

示例源码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
{
	//定义实例变量
	@public
	NSString * _name;
	int _age;
}
@end
@implementation OCPerson
@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//创建成员变量
		OCPerson *p =[[OCPerson alloc] init];
		//通过指针变量访问两个实例变量
		NSLog(@"p->_name : %@ , p->_age : %d", p->_name, p->_age);

		//通过指针访问变量为两个成员变量赋值
		p->_name = @"Jim";
		p->_age = 18;
		NSLog(@"p->_name : %@ , p->_age : %d", p->_name, p->_age);

		OCPerson *p1 = [[OCPerson alloc] init];
		OCPerson *p2 = [[OCPerson alloc] init];
		p1->_name = @"Tom";
		p2->_name = @"Keen";
		NSLog(@"p1->_name : %@ , p2->_name : %@", p1->_name, p2->_name);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 09:17:38.510 a.out[2858:507] p->_name : (null) , p->_age : 0
2015-09-30 09:17:38.513 a.out[2858:507] p->_name : Jim , p->_age : 18
2015-09-30 09:17:38.513 a.out[2858:507] p1->_name : Tom , p2->_name : Keen
octopus-2:oc_object octopus$ 

(4) 成员变量 源码示例解析

示例解析 :

-- 成员变量初始化 : 创建对象时系统会为为成员变量赋初始值, 基础类型 0, 指针类型 nil;

-- 初始化过程 : 执行 "OCPerson *p1 = [[OCPerson alloc] init]" 语句时 系统为 OCPerson 对象的成员变量分配内存空间, 并初始化, 并将对象赋给 p1 变量;

2. 模拟类变量

前提 : Object-C 不支持 类变量, 但是可以使用 全局变量来模拟类变量;

static 关键字 : 不能修饰成员变量, 只能修饰 局部变量 和 全局变量;

-- 修饰局部变量 : 将局部变量存储到静态存储区;

-- 修饰全局变量 : 限制全局变量只能在当前文件中访问;

-- 修饰函数 : 限制该函数只能在当前源文件中访问;

模拟类变量方式 : static 修饰全局变量, 提供一个类方法暴露该全局变量, 并提供一个类方法暴露该全局变量;

代码示例 : 根据上面的示例代码修改而来;

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
{
	//定义实例变量
	@public
	NSString * _name;
	int _age;
}

/*
 * 声明的 类方法 用于获取类变量
 */
+ (NSString *) getInstance;
/*
 * 声明的 类方法 用于设置类变量
 */
+ (void) setInstance : (NSString *) instanc;
@end
@implementation OCPerson

/*
 * 定义的类变量, 使用 static 修饰全局变量
 */
static NSString * _instance = nil;

/**
 * 获取类变量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 设置类变量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//创建成员变量
		OCPerson *p =[[OCPerson alloc] init];
		//通过指针变量访问两个实例变量
		NSLog(@"p->_name : %@ , p->_age : %d", p->_name, p->_age);

		//通过指针访问变量为两个成员变量赋值
		p->_name = @"Jim";
		p->_age = 18;
		NSLog(@"p->_name : %@ , p->_age : %d", p->_name, p->_age);

		OCPerson *p1 = [[OCPerson alloc] init];
		OCPerson *p2 = [[OCPerson alloc] init];
		p1->_name = @"Tom";
		p2->_name = @"Keen";
		NSLog(@"p1->_name : %@ , p2->_name : %@", p1->_name, p2->_name);

		[OCPerson setInstance : @"INSTANCE"];
		NSLog(@"instance : %@", [OCPerson getInstance]);
	}
}

-- 执行结果 : 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 10:14:35.235 a.out[2955:507] p->_name : (null) , p->_age : 0
2015-09-30 10:14:35.237 a.out[2955:507] p->_name : Jim , p->_age : 18
2015-09-30 10:14:35.238 a.out[2955:507] p1->_name : Tom , p2->_name : Keen
2015-09-30 10:14:35.238 a.out[2955:507] instance : INSTANCE
octopus-2:oc_object octopus$ 

3. 单例模式

实现单例 : 定义一个 static 全局变量, 该变量用于保存自己创建的 Singleton 对象, 每次程序获取该单例时, 判断 static singleton 是否为nil, 全局变量为 nil 初始化一个实例并赋值给 static 变量;

单例模式源码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
{
	//定义实例变量
	@public
	NSString * _name;
	int _age;
}

/*
 * 声明的 类方法 用于获取类变量
 */
+ (NSString *) getInstance;
/*
 * 声明的 类方法 用于设置类变量
 */
+ (void) setInstance : (NSString *) instanc;

/**
 * 单例对象获取方法
 */
+ (OCPerson *) getSingleton;

@end
@implementation OCPerson

/*
 * 定义的类变量, 使用 static 修饰全局变量
 */
static NSString * _instance = nil;

/*
 * 定义单例对象类
 */
static OCPerson * singleton = nil;

/**
 * 获取类变量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 设置类变量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

/**
 * 获取单例对象
 */
+ (OCPerson *) getSingleton
{
	if(!singleton)
	{
		singleton = [[OCPerson alloc] init];
	}
	return singleton;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//创建成员变量
		OCPerson *p =[[OCPerson alloc] init];
		//通过指针变量访问两个实例变量
		NSLog(@"p->_name : %@ , p->_age : %d", p->_name, p->_age);

		//通过指针访问变量为两个成员变量赋值
		p->_name = @"Jim";
		p->_age = 18;
		NSLog(@"p->_name : %@ , p->_age : %d", p->_name, p->_age);

		/*
		 * 创建两个变量
		 */
		OCPerson *p1 = [[OCPerson alloc] init];
		OCPerson *p2 = [[OCPerson alloc] init];
		p1->_name = @"Tom";
		p2->_name = @"Keen";
		NSLog(@"p1->_name : %@ , p2->_name : %@", p1->_name, p2->_name);

		/*
		 * 设置并获取静态变量
		 */
		[OCPerson setInstance : @"INSTANCE"];
		NSLog(@"instance : %@", [OCPerson getInstance]);

		/*
		 * 获取两次单例对象, 比较这两个对象是否相等
		 */
		NSLog(@"%d", [OCPerson getSingleton] == [OCPerson getSingleton]);
	}
}

-- 执行效果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 10:31:38.935 a.out[2974:507] p->_name : (null) , p->_age : 0
2015-09-30 10:31:38.937 a.out[2974:507] p->_name : Jim , p->_age : 18
2015-09-30 10:31:38.938 a.out[2974:507] p1->_name : Tom , p2->_name : Keen
2015-09-30 10:31:38.939 a.out[2974:507] instance : INSTANCE
2015-09-30 10:31:38.939 a.out[2974:507] 1

三. 隐藏 封装

1. 封装简介

封装 : 面向对象三个特性 封装, 继承, 多态;

-- 概念 : 将对象的 状态信息 隐藏在对象内部, 不允许外界 直接访问内部信息, 外部只能通过 类提供的方法 来实现对内部信息的访问 操作;

2. 访问控制符使用详解

(1) 访问控制符界别范围

访问控制符控制级别 : @private < (@package | @protected) < @public ;

-- @private : 只能在当前类访问, 用于彻底隐藏成员变量, 类实现部分定义的成员变量默认是 @private ;

-- @package : 只能在当前映像访问, 可以在当前类 或者 当前映像的 任意位置访问, 用于部分隐藏成员变量;

-- @protected : 子类访问, 可以在当前类, 子类 任意位置访问, 类接口部分定义的成员变量默认使用 @protected 访问;

-- @public : 可以在任意位置访问;

访问控制符范围 :


@private @package @protected @public
同一个类中 可访问 可访问 可访问 可访问
通映像中
可访问
可访问
子类中

可访问 可访问
全局任意位置


可访问

(2) 访问控制符注意点 和 原则

访问控制符注意点 :

-- 注意 : 访问控制符只能控制成员变量是否可以被其它类访问, 不能用于修饰局部变量;

-- 访问控制符控制范围 : 从访问控制符出现位置开始 到 下一个访问控制符 或者 花括号之间的成员变量;

-- getter 和 setter 方法 : 去掉成员变量的下划线前缀, _name 对应 setName() name();

基本原则 :

-- 修饰成员变量方法 : 类中 99% 的变量都应该使用 @private 控制, 用于辅助实现类其它方法的工具方法也要使用 @private 修饰, 定义在实现类内部;

-- 子类访问 : 父类希望其成员变量能被子类访问, 使用 @protected 控制该成员变量;

-- 接口默认 public 方法 : 暴露给其它类*调用的方法, 在类接口中定义, 在类实现中实现它们;

(3) 访问控制符源码示例

示例源码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
{
	/*
	 * 定义示例变量, 将这些变量定义成 private 类型
	 * 该实例变量只能在 OCPerson.m 中使用
	 */
	@private
	NSString * _name;
	int _age;
}

/*
 * 下面得四个方法是存取方法
 */
- (void) setName : (NSString *) name;
- (NSString *) name;
- (void) setAge : (int) age;
- (int) age;

/*
 * 声明的 类方法 用于获取类变量
 */
+ (NSString *) getInstance;
/*
 * 声明的 类方法 用于设置类变量
 */
+ (void) setInstance : (NSString *) instanc;

/**
 * 单例对象获取方法
 */
+ (OCPerson *) getSingleton;

@end
@implementation OCPerson

/*
 * 定义的类变量, 使用 static 修饰全局变量
 */
static NSString * _instance = nil;

/*
 * 定义单例对象类
 */
static OCPerson * singleton = nil;

- (void) setName : (NSString *) name
{
	_name = name;
}

- (NSString *) name
{
	return _name;
}

- (void) setAge : (int) age
{
	_age = age;
}

- (int) age
{
	return _age;
}

/**
 * 获取类变量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 设置类变量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

/**
 * 获取单例对象
 */
+ (OCPerson *) getSingleton
{
	if(!singleton)
	{
		singleton = [[OCPerson alloc] init];
	}
	return singleton;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		/*
		 * 获取单例对象
		 */
		OCPerson * p = [OCPerson getSingleton];

		/*
		 * 使用 setter 方法设置 _name 和 _age 成员变量
		 */
		[p setAge : 18];
		[p setName : @"Tom"];

		/*
		 * 使用 getter 方法 age() 和 name() 获取 _age 和 _name 成员变量值
		 */
		NSLog(@"name : %@ , age : %d", [p name], [p age]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 11:45:13.251 a.out[3096:507] name : Tom , age : 18

3. @package 访问控制符简介

常用的访问控制符 : @private 将成员变量限制在当前类内部, @public 彻底暴露成员变量, @protected 让成员变量在子类中可以访问;

映像 : 编译后生成的框架 和 执行文件, 编译后 @package 修饰的成员变量 在这些 框架 和 可执行文件中可以被任意访问;

-- 示例 : 我们之前经常使用类似命令 clang -fobjc-arc -framework Foundation OCPerson.m , 该命令生成一个 a.out 文件, 该 a.out 就是一个映像;

4. 合成存取方法

(1) @property 和 @synthesize 指令

自动 合成 getter 和 setter 方法 :

-- 合成方法 : 接口部分使用 @property 指令定义属性, 如 @property int age; 实现部分使用 @synthesize 声明, 如 @synthesize age;

-- 生成成员变量 : 使用 @property 和 @synthesize 指令声明属性后, 会合成对应的 getter 和 setter 方法, 自动在类实现部分定义一个与 getter 方法同名的成员变量;

@synthesize 指令 : @property NSString * name; @synthesize name = _name;

-- 上面的代码作用 : 使用 @property 合成的存取方法对应的是 _name, 不是 name;

-- @synthesize 语法 : @synthesize propertyName [= fileName];

-- 默认成员变量 : @synthesize  不指定 filedName 时, 成员变量默认为属性名添加下划线; 如 @property NSString * name; @synthesize name; 默认对应的成员变量是 name; 如果 @synthesize name = _name; 默认对应的成员变量是 _name;

示例源码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
/*
 * 定义示例变量, 将这些变量定义成 private 类型
 * 该实例变量只能在 OCPerson.m 中使用
 */
@property (nonatomic) NSString * name;
@property int age;

/*
 * 声明的 类方法 用于获取类变量
 */
+ (NSString *) getInstance;
/*
 * 声明的 类方法 用于设置类变量
 */
+ (void) setInstance : (NSString *) instanc;

/**
 * 单例对象获取方法
 */
+ (OCPerson *) getSingleton;

@end
@implementation OCPerson

/*
 * 定义的类变量, 使用 static 修饰全局变量
 */
static NSString * _instance = nil;

/*
 * 定义单例对象类
 */
static OCPerson * singleton = nil;

/*
 * 如果 没有后面的 = _name 和 = _age 提示,默认的成员变量 是 name 和 age
 * 当前设置对应的默认成员变量是 _name 和 _age
 */
@synthesize name = _name;
@synthesize age = _age;

- (void) setName : (NSString *) name
{
	//默认的成员变量是 _name
	self->_name = [NSString stringWithFormat : @"++%@++", name];
}

/**
 * 获取类变量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 设置类变量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

/**
 * 获取单例对象
 */
+ (OCPerson *) getSingleton
{
	if(!singleton)
	{
		singleton = [[OCPerson alloc] init];
	}
	return singleton;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		/*
		 * 获取单例对象
		 */
		OCPerson * p = [OCPerson getSingleton];

		/*
		 * 使用 setter 方法设置 _name 和 _age 成员变量
		 */
		[p setAge : 18];
		[p setName : @"Tom"];

		/*
		 * 使用 getter 方法 age() 和 name() 获取 _age 和 _name 成员变量值
		 */
		NSLog(@"name : %@ , age : %d", [p name], [p age]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ ./a.out
2015-09-30 13:29:26.323 a.out[3198:507] name : ++Tom++ , age : 18

5. @Property 特殊指示符

(1) assign 特殊指示符

assign 指示符 :

-- 作用 : 指定对属性只是简单赋值, 不更改引用计数, 主要适用于 NSInteger int short double 结构体 等数据类型;

-- 引用计数 : 对象的引用计数大于 0 时, 该对象不会被回收, 基础数据类型不存在回收问题, 可以使用 assign 指示符;

(2) atomic (nonatomic) 特殊指示符

atomic (nonatomic) 指示符 :

-- 作用 : 指定合成的存取方法是否是原子操作, 即线程是否安全;

-- atomic : 合成的存取方法都是线程安全的, 一个线程调用存取方法时, 其它方法无法调用存取方法, 避免多线程并发破坏对象的数据完整性;

-- nonatomic : 用于提高存取方法的访问性能, atomic 线程安全会造成性能下降;

(3) copy 特殊指示符

copy 指示符 :

-- 作用 : 如果使用 copy 指示符, 当调用 setter 方法对成员变量赋值时, 现将被赋值对象复制一个副本, 再将该副本赋给成员变量;

-- 引用计数 : copy 会将原成员变量所引用计数 -1;

-- 适用情况 : 成员变量类型是指针类型时, 被赋值的对象有可能在赋值之后被修改, 如果不想让被赋值对象被修改影响成员变量, 可以使用 copy 指示符;

-- 代码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
/*
 * 定义示例变量, 将这些变量定义成 private 类型
 * 该实例变量只能在 OCPerson.m 中使用
 */
@property (nonatomic) NSString * name;
@property (nonatomic, copy) NSString * describe;
@property int age;
@end
@implementation OCPerson

/*
 * 如果 没有后面的 = _name 和 = _age 提示,默认的成员变量 是 name 和 age
 * 当前设置对应的默认成员变量是 _name 和 _age
 */
@synthesize name = _name;
@synthesize describe = _describe;
@synthesize age = _age;

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//初始化对象
		OCPerson * p = [[OCPerson alloc] init];
		//创建一个字符串
		NSMutableString *str = [NSMutableString stringWithString : @"Tom"];
		//将字符串设置给对象的 name 属性
		[p setName:str];
		[p setDescribe:str];
		//打印出name成员变量值
		NSLog(@"1.name : %@, describe : %@", [p name], [p describe]);

		//修改被赋值的对象
		[str appendString : @" Hax"];
		//打印出name成员变量值
		NSLog(@"2.name : %@, describe : %@", [p name], [p describe]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 18:05:48.170 a.out[3295:507] 1.name : Tom, describe : Tom
2015-09-30 18:05:48.172 a.out[3295:507] 2.name : Tom Hax, describe : Tom

(4) getter setter 特殊指示符

getter(setter) 指示符 :

-- 作用 : getter 和 setter 方法指定自定义方法名;

-- 示例 : getter = han 指定 getter 方法为 han, setter = octopus 指定 setter 方法为 octopus;

-- 源码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
/*
 * 定义示例变量, 将这些变量定义成 private 类型
 * 该实例变量只能在 OCPerson.m 中使用
 */
@property (nonatomic) NSString * name;
@property (nonatomic, copy) NSString * describe;
@property (assign, nonatomic, getter = han, setter = octopus:) int age;
@end
@implementation OCPerson

/*
 * 如果 没有后面的 = _name 和 = _age 提示,默认的成员变量 是 name 和 age
 * 当前设置对应的默认成员变量是 _name 和 _age
 */
@synthesize name = _name;
@synthesize describe = _describe;
@synthesize age = _age;

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCPerson * p = [[OCPerson alloc] init];
		NSMutableString *str = [NSMutableString stringWithString : @"Tom"];
		[p setName:str];
		[p setDescribe:str];
		NSLog(@"1.name : %@, describe : %@", [p name], [p describe]);

		[str appendString : @" Hax"];
		NSLog(@"2.name : %@, describe : %@", [p name], [p describe]);

		[p octopus:18];
		NSLog(@"age : %d", [p han]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 18:28:16.132 a.out[3329:507] 1.name : Tom, describe : Tom
2015-09-30 18:28:16.135 a.out[3329:507] 2.name : Tom Hax, describe : Tom
2015-09-30 18:28:16.135 a.out[3329:507] age : 18

(5) readonly readwrite 特殊指示符

readonly : 系统只合成 getter 方法, 不再合成 setter 方法;

readwrite : 需要合成 setter getter 方法;

(6) retain 特殊指示符

retain :

-- 作用 : retain 定义属性, 将某个对象赋值给该属性时, 该属性原来所引用的对象引用计数 -1, 被赋值对象 (成员变量) 引用计数 +1;

-- 使用场景 : 在未启用 ARC 机制情况下, 常用, 启用后不常用;

-- 源码示例 : 不能使用 @autoreleasepool ARC 机制, 需要关闭该机制;

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, retain) NSDate * date;
@end
@implementation OCPerson
@synthesize date;
@end

int main(int argc, char * argv[])
{
	OCPerson * p = [[OCPerson alloc] init];
	NSDate * date = [[NSDate alloc] init];

	NSLog(@"date retainCount : %ld, _date retainCount : %ld", date.retainCount, [p date].retainCount);
	[p setDate : date];
	NSLog(@"date retainCount : %ld, _date retainCount : %ld", date.retainCount, [p date].retainCount);
	[date release];
	NSLog(@"date retainCount : %ld, _date retainCount : %ld", date.retainCount, [p date].retainCount);
}

-- 执行结果 : 注意编译时不能使用 -fobjc-arc 参数;

octopus-2:oc_object octopus$ clang -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 18:59:11.602 a.out[3400:507] date retainCount : 1, _date retainCount : 0
2015-09-30 18:59:11.604 a.out[3400:507] date retainCount : 2, _date retainCount : 2
2015-09-30 18:59:11.605 a.out[3400:507] date retainCount : 1, _date retainCount : 1

(7) strong weak 特殊指示符

strong 指示符 : 指定该属性对赋值对象持有强引用, 只要该强引用指向被赋值的对象, 那么该对象就不会自动回收;

weak 指示符 : 指定该属性对被赋值对象持有弱引用, 弱引用指向被赋值的对象, 该对象可能被回收;

(8) unsafe_unretained 特殊指示符

unsafe_unretained 指示符 : 与 weak 指示符基本相似, 对于被 unsafe_unretained 指向的对象也可能会被回收; 被 unsafe_unretained 修饰的指示的指针变量, 该指针不会被赋值为 nil, 可能导致程序崩溃;

5. 使用 . 语法访问属性

(1) 变量分类

点 . 使用 :

-- 使用前提 : 使用 @property @synthesize 合成 setter 和 getter 方法; 实际上 也允许使用 . 语法访问属性 和 对属性赋值;

-- 本质 : 点语法是一种简单写法, 其本质仍然是 getter 和 setter 方法;

-- 获取属性值 : 只要对象有 getter 方法, 程序可以使用 点 语法获取属性值;

-- 设置属性值 : 只要对象 setter 方法, 程序可以使用 点 语法获取属性值;

-- 源码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
@end
@implementation OCPerson
@synthesize name;
@synthesize age;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		p.name = @"Tom";
		p.age = 18;

		NSLog(@"name : %@, age : %d", p.name, p.age);
	}
}

-- 执行结果 :

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 19:34:59.849 a.out[3451:507] name : Tom, age : 18

四. 键值编码 (KVC)

1. 简单地 KVC

(1) KVC 简介

KVC 简介 :

-- 引入 : Object-C 可以通过 getter setter 方法操作属性, 还可以 以字符串形式间接操作属性, 该方式是 Key Value Coding (KVC);

-- KVC 使用前提 : 最好在接口部分使用 @property 实现类部分使用 @synthesize 合成存取方法, 也可以只定义 "_属性名" 或 "属性名" 成员变量, 之后才能成功使用 KVC;

操作属性方法 :

-- 未指定属性设定值 : "setValue : 属性值 forKey : 属性名" ;

-- 获取指定属性值 : "valueForKey : 属性名" ;

(2) setValue : forKey 执行机制

"setValue : 属性值 forKey 属性名" 执行机制 :

-- 调用 setter 方法 : 优先考虑调用 "setName : 属性值", 通过 setter 方法完成赋值;

-- 寻找 "_属性名" 变量 : 如果没有 setter 方法, 系统会搜索 "_属性名" 成员变量, 只要有 "_属性名" 成员变量, 无论实在 接口 还是在实现类定义, 无论使用什么访问控制符, 系统都会对该变量赋值;

-- 寻找 "属性名" 变量 : 如果既没有 setter 方法, 也没有 "_属性名" 成员变量, 系统会搜索 "属性名" 成员变量, 不管定义在什么位置(接口 实现), 也不管是用什么访问控制符修饰的, 系统都会对该变量赋值;

-- 引发异常 : 上面三种都没有成功, 系统执行 该对象的 "setValue : forUndefinedKey : " 方法;

(3) valueForKey : 执行机制

"valueForKey : 属性名" 执行机制 :

-- 调用 getter 方法 : 优先考虑调用 getter 方法, 即 属性名() 方法获取返回值;

-- 寻找 "_属性名" 成员变量 : 如果没有 getter 方法, 系统会搜索 "_属性名" 成员变量, 不管该成员变量的定义位置 和 用什么访问控制符修饰, 都返回该 "_属性名" 成员变量值;

-- 寻找 "属性名" 成员变量 : 如果既没有找到 getter 方法, 也没有找到 "_属性名" 成员变量, 系统会搜索 "属性名" 成员变量, 不管该成员变量的定义位置 和 用什么访问控制符修饰, 都返回 "属性名" 成员变量的值;

-- 引发异常 : 上面三种都没有成功, 系统会执行对象的 "valueForUndefinedKey :";

(4) KVC 简单用法源码示例

源码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
{
	@private
	int _age;
	@public
	NSString * family;
}
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : @"Tom" forKey : @"name"];
		[p setValue : [NSNumber numberWithInt : 18] forKey : @"age"];
		[p setValue : @"Hax" forKey : @"family"];

		NSLog(@"name : %@, age : %@, family : %@",
			[p valueForKey : @"name"],
			[p valueForKey : @"age"],
			[p valueForKey : @"family"]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 21:59:44.909 a.out[3566:507] name : Tom, age : 18, family : Hax

2. Key 不存在的情况处理

(1) 情况简介

前提 : KVC 操作时, 如果遇到 既没有 getter setter 方法, 也没有 "_属性值" 或者 "属性值" 成员变量时, KVC 会调用 "setValue : forUndefinedKey :" 和 "valueForUndefinedKey :" 方法, 系统只是定义了两个默认方法, 但是并没有执行实际有意义的内容;

(2) 异常代码示例

示例代码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : @"Jack" forKey : @"son"];
		[p valueForKey : @"son"];
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 22:21:58.642 a.out[3611:507] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<OCPerson 0x7fecd0c09610> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key son.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8e37625c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8cc74e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8e375e09 -[NSException raise] + 9
	3   Foundation                          0x00007fff8d83d72e -[NSObject(NSKeyValueCoding) setValue:forKey:] + 389
	4   a.out                               0x000000010e9fae15 main + 117
	5   libdyld.dylib                       0x00007fff8318c5fd start + 1
	6   ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

(3) 处理 setValue : forKey 异常代码示例

代码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@"You want set value %@ to %@ key", value, key);
}
-(void) valueForUndefinedKey : (id) key
{
	NSLog(@"You want get the value of %@ key", key);
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : @"Jack" forKey : @"son"];
		[p valueForKey : @"son"];
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 22:27:17.699 a.out[3623:507] You want set value Jack to son key
2015-09-30 22:27:17.703 a.out[3623:507] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<OCPerson 0x7fd162408cf0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key son.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8e37625c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8cc74e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8e375e09 -[NSException raise] + 9
	3   Foundation                          0x00007fff8d90e14e -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 235
	4   Foundation                          0x00007fff8d825466 -[NSObject(NSKeyValueCoding) valueForKey:] + 381
	5   a.out                               0x00000001049d6ddc main + 140
	6   libdyld.dylib                       0x00007fff8318c5fd start + 1
	7   ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

(4) 处理 valueForKey 异常代码示例

代码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@"You want set value %@ to %@ key", value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@"You want get the value of %@ key", key);
	return nil;
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p valueForKey : @"son"];
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 22:33:43.557 a.out[3652:507] You want get the value of son key

2. 处理 nil 值

(1) 情况简介

问题引入 : 使用 KVC 设置对象属性, 如果属性是指针类型, 设置 nil 值完全正确, 如果为 基本类型 int short 类型设置了 nil 会出现异常;

-- 异常信息 : 为基本类型设置 nil 会爆出 NSInvalidArgumentException 异常, int 类型不能接受 nil 值;

-- "setNilValueForKey :" 方法 : 为成员变量设置 nil 时, 如果该成员变量不接受 nil 值, 系统会自动执行 setNilValueForKey 方法;

-- 定制 "setNilValueForKey :" 方法 : 重写了该方法之后, 如果再试图给 不接受 nil 值的变量赋值 nil, 就会自动调用该方法;

(2) 异常示例代码

示例代码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;
@end
@implementation OCPerson
@synthesize name;
@synthesize age;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@"You want set value %@ to %@ key", value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@"You want get the value of %@ key", key);
	return nil;
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : nil forKey : @"age"];
		NSLog(@"age : %@", [p valueForKey : @"age"]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 23:11:56.652 a.out[3920:507] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<OCPerson 0x7ffe0b409870> setNilValueForKey]: could not set nil as the value for the key age.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8e37625c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8cc74e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8e37610c +[NSException raise:format:] + 204
	3   Foundation                          0x00007fff8d90e2a7 -[NSObject(NSKeyValueCoding) setNilValueForKey:] + 73
	4   Foundation                          0x00007fff8d83d72e -[NSObject(NSKeyValueCoding) setValue:forKey:] + 389
	5   a.out                               0x000000010da0ad38 main + 120
	6   libdyld.dylib                       0x00007fff8318c5fd start + 1
	7   ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

(3) 异常处理示例代码

异常处理示例代码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;
@end
@implementation OCPerson
@synthesize name;
@synthesize age;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@"You want set value %@ to %@ key", value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@"You want get the value of %@ key", key);
	return nil;
}
-(void) setNilValueForKey : (id) key
{
	if([key isEqualToString : @"age"])
	{
		age = 0;
	}else
	{
		[super setNilValueForKey : key];
	}
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : nil forKey : @"age"];
		NSLog(@"age : %@", [p valueForKey : @"age"]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 23:19:27.980 a.out[3934:507] age : 0

3. Key 路径

(1) 复合属性

复合属性 : 在类 OCStudent 中 定义了 OCPerson 成员变量, 如果我们想要访问 OCPerson 中得 name 成员变量, 就需要先访问 OCPerson 成员变量, 再访问 name 成员变量;

Key 路径操作方法 :

-- " setValue : forKeyPath : " 方法 : 根据 key 路径设置属性值;

-- " valueForKeyPath : " 方法 :  根据 key 路径获取属性值;

KVC 优缺点 :

-- 缺点 : KVC 操作对象性能要比使用 getter 和 setter 方法要差;

-- 优点 : KVC 使用会使程序更加简洁, 适合提炼通用代码, 具有极高的灵活性;

(2) Key 路径示例代码

示例代码 :

-- OCPerson.h :

/*************************************************************************
    > File Name: OCPerson.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 23:39:06 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;
@end

-- OCPerson.m :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/
#import "OCPerson.h"

@implementation OCPerson
@synthesize name;
@synthesize age;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@"You want set value %@ to %@ key", value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@"You want get the value of %@ key", key);
	return nil;
}
-(void) setNilValueForKey : (id) key
{
	if([key isEqualToString : @"age"])
	{
		age = 0;
	}else
	{
		[super setNilValueForKey : key];
	}
}
@end

-- OCOrder.h

/*************************************************************************
    > File Name: OCOrder.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 23:37:51 2015
 ************************************************************************/
#import "OCPerson.h"
@interface OCOrder : NSObject
@property (nonatomic, strong) OCPerson * person;
@end

-- OCOrder.m

/*************************************************************************
    > File Name: OCOrder.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 23:43:08 2015
 ************************************************************************/
#import "OCOrder.h"
@implementation OCOrder
@synthesize person;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCOrder * order = [[OCOrder alloc] init];
		OCPerson * p = [[OCPerson alloc] init];

		[order setValue : p forKey : @"person"];

		[order setValue : @"Tom" forKeyPath : @"person.name"];
		NSLog(@"name : %@", [order valueForKeyPath : @"person.name"]);
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m OCOrder.m
octopus-2:oc_object octopus$ ./a.out
2015-09-30 23:56:44.338 a.out[4031:507] name : Tom

五. 键值监听 (KVO)

1. KVO 简介

IOS 需求 :

-- 数据模型组件 : 负责维护应用程序的状态数据;

-- 视图组件 : 负责显示数据模型组件内部的状态数据;

-- 需求 : 数据模型组件数据发生变化时, 视图组件能动态更新;

KVO (Key Value Observing) 键值监听 :

-- 适用前提 : 只要是 NSObject 子类就可以使用 KVO;

-- 注册监听器用于监听 key 路径 : "addObserver : forKeyPath : options : context";

-- 为 Key 路径删除指定监听器 : "removeObserver : forKeyPath :" 或者 "removeObserver : forKeyPath :";

-- 回调 : 数据发生变化时, 会回调 "observerValueForKeyPath : ofObject : change : context" 方法;

KVO 编程步骤 :

-- 注册监听器 : 为被监听对象注册监听器, 使用 "addObserver : forKeyPath : options : context";

-- 重写监听器方法 : 重写 "observeValueForKeyPath : ofObject : change : contex : " 方法;

2. KVO 代码示例

KVO 代码示例 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@interface OCPerson : NSObject
@property (nonatomic, weak) NSString * name;
@property (nonatomic, assign) int age;
@end

@implementation OCPerson
@synthesize name;
@synthesize age;

- (void) setName : (NSString *) str
{
	self->name = str;
	[self addObserver : self forKeyPath : @"name" options : NSKeyValueObservingOptionNew context : nil];
}

- (void) setAge : (int) old
{
	self->age = old;
	[self addObserver : self forKeyPath : @"age" options : NSKeyValueObservingOptionNew context : nil];
}

- (void) observeValueForKeyPath : (NSString *) keyPath
					   ofObject : (id) object
						 change : (NSDictionary *) change
						context : (void *) context
{
	NSLog(@"keyPath : %@, object : %@, change : %@", keyPath, object, [change objectForKey : @"new"]);
}

- (void) dealloc
{
	NSLog(@"dealloc callback");
	[self removeObserver : self forKeyPath : @"name"];
	[self removeObserver : self forKeyPath : @"age"];
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * person = [[OCPerson alloc] init];
		person.age = 20;
		person.name = @"Tom";

		person.age = 18;
		person.name = @"Jerry";

		[person removeObserver : person forKeyPath : @"name"];
		[person removeObserver : person forKeyPath : @"age"];
	}
}

-- 执行结果

bogon:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
bogon:oc_object octopus$ ./a.out
2015-10-01 20:36:39.722 a.out[734:507] keyPath : age, object : <OCPerson: 0x7fac8a4042f0>, change : 18
2015-10-01 20:36:39.724 a.out[734:507] keyPath : name, object : <OCPerson: 0x7fac8a4042f0>, change : Jerry
2015-10-01 20:36:39.724 a.out[734:507] dealloc callback

六. Object-C 对象初始化

1. 实例空间分配过程

创建对象语法 : [[类名 alloc] init] 语法, [类名 new] 语法, 每次创建对象都要调用该对象的 alloc 方法为对象分配内存空间;

-- alloc 方法 : alloc 方法 是在 NSObject 中定义的, 所有的 OC 对象都是 NSObject 的子类, 所有的类都可以调用 alloc 方法为所有的实例变量分配内存;

-- init 方法 : 来自 NSObject 中定义, 所有的对象调用 init 方法进行初始化, 将每个成员变量内存空间赋值为 0, 所有的整型变量所在空间都重置为 0, 浮点型变量 0.0, BOOL 型变量 NO, 指针型 nil;

-- 注意 : 使用 [类名 init] 创建的对象也可以执行, 但是没有进行 init 初始化, 可能出现未知结果;

2. 初始化方法 与 对象初始化

(1) 重写初始化方法

初始化方法种类 :

-- 默认初始化 : NSObject 提供的 默认的 init 方法为所有成员变量赋值 0 初始值;

-- 常用初始化 : 重写 NSObject 的 init 方法, 可以加入任意的自定义初始化过程;

- (id) init
{
	if(self = [super init])
	{
		self.name = @"Tom";
		self.age = 18;
		self.address = @"DC";
	}
	return self;
}

-- 默认初始化调用

OCPerson * person1 = [[OCPerson alloc] init];

-- 多个初始化方法 : 还可以自定义 initXxx 方法, 可以根据参数执行自定义初始化;

- (id) initWithHobby : (NSString *) hobby_value
{

	if(self = [super init])
	{
		self.name = @"Jerry";
		self.age = 20;
		self.address = @"ST";
		self.hobby = hobby_value;;
	}
	return self;
}

-- 其它初始化方法调用

OCPerson * person2 = [[OCPerson alloc] initWithHobby : @"football"];

(2) 初始化方法示例代码

示例代码 :

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString * address;
@property (nonatomic, copy) NSString * hobby;

- (id) initWithHobby : (NSString *) hobby_value;
@end

@implementation OCPerson
@synthesize name;
@synthesize age;
@synthesize address;
@synthesize hobby;

- (id) init
{
	if(self = [super init])
	{
		self.name = @"Tom";
		self.age = 18;
		self.address = @"DC";
	}
	return self;
}

- (id) initWithHobby : (NSString *) hobby_value
{

	if(self = [super init])
	{
		self.name = @"Jerry";
		self.age = 20;
		self.address = @"ST";
		self.hobby = hobby_value;;
	}
	return self;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * person1 = [[OCPerson alloc] init];
		NSLog(@"name : %@, age : %d, address : %@, hobby : %@", person1.name, person1.age, person1.address, person1.hobby);

		OCPerson * person2 = [[OCPerson alloc] initWithHobby : @"football"];
		NSLog(@"name : %@, age : %d, address : %@, hobby : %@", person2.name, person2.age, person2.address, person2.hobby);
	}
}

-- 执行结果 :

bogon:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
bogon:oc_object octopus$ ./a.out
2015-10-01 21:47:06.409 a.out[832:507] name : Tom, age : 18, address : DC, hobby : (null)
2015-10-01 21:47:06.410 a.out[832:507] name : Jerry, age : 20, address : ST, hobby : football

七. 类的继承

1. OC 类继承简介

继承简介
-- OC 继承 : OC 继承是单继承, 一个子类只能有一个父类, 这点与 Java 相同;
-- 子类继承父类格式 : 只需要在接口部分声明类时, 在类名后面加上 ": SuperClass" 即可;

@interface SubClass : SuperClass
{
	//成员变量
}
//方法
@end

-- 子类收获 : 子类扩展父类时, 子类可以得到父类的 全部方法 和 全部成员变量;

super 关键字
-- 作用 : 在子类方法调用父类被覆盖的实例方法, 该关键字用于限定对象调用其从父类获得的属性 和 方法;
-- 注意 : super 关键字不能出现在 类方法中, 因为类方法执行是不依靠对象的;
-- self 对比 : self 也不能出现在类方法中;

2. OC 类继承 源码示例

源码示例 :

-- OCAnimal.h : 父类的接口头文件, 定义了两个属性;

/*************************************************************************
    > File Name: OCAnimal.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 五 10/ 2 13:21:19 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@interface OCAnimal : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;

-(void) info;
@end

-- OCAnimal.m : 父类的实现类, 声明了接口中得两个属性, 实现了一个方法;

/*************************************************************************
    > File Name: OCAnimal.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 五 10/ 2 13:23:37 2015
 ************************************************************************/
#import "OCAnimal.h"

@implementation OCAnimal
@synthesize name;
@synthesize age;

- (void) info
{
	NSLog(@"name : %@, age : %d", name, age);
}

@end

-- OCCat.h : 子类的接口头文件, 定义了新的成员变量 和 方法;

/*************************************************************************
    > File Name: OCCat.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 五 10/ 2 13:28:22 2015
 ************************************************************************/
#import "OCAnimal.h"

@interface OCCat : OCAnimal
@property (nonatomic, copy) NSString * color;
- (void) purr;
@end

-- OCCat.m : 子类的实现, 此外还 重写了 父类 info 方法;

/*************************************************************************
    > File Name: OCCat.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 五 10/ 2 13:34:05 2015
 ************************************************************************/
#import "OCCat.h"

@implementation OCCat
@synthesize color;

- (void) info
{
	[super info];
	NSLog(@"color : %@", color);
}

- (void) purr
{
	NSLog(@"%@ 喵喵", super.name);
}

@end

-- OCCatTest.m : 子类测试类;

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 五 10/ 2 13:37:16 2015
 ************************************************************************/
#import "OCCat.h"

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCCat * cat = [[OCCat alloc] init];
		cat.name = @"小花";
		cat.age = 18;
		cat.color = @"red";
		[cat info];
		[cat purr];
	}
}

-- 执行结果

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCAnimal.m OCCat.m OCCatTest.m
octopus-2:oc_object octopus$ ./a.out
2015-10-02 13:43:08.575 a.out[696:507] name : 小花, age : 18
2015-10-02 13:43:08.576 a.out[696:507] color : red
2015-10-02 13:43:08.577 a.out[696:507] 小花 喵喵

八. OC 多态

1. OC 类继承简介

(1) 编译运行时类型

指针变量类型 : 如果编译时与运行时类型不同, 就会产生多态;
-- 编译时类型 : 由声明该变量时使用的类型决定; 
-- 运行时类型 : 由实际赋值给该变量的类型决定;

(2) 赋值多态

赋值多态 : 子类可以在任意位置替换父类 (里氏替换);
-- 多态出现 : 子类赋值给父类时, 编译时类型是父类, 运行时类型是子类; 
-- 调用重写方法 : 调用子类重写父类的方法时, 执行的是父类方法;
-- 多态 : 相同类型的变量调用同一个方法, 会出现不同的特征, 这就是多态;

(3) 指针变量强制类型转换

指针变量强制类型转换
-- 问题出现 : 将子类赋值给父类类型对象时, 就不能再使用父类对象调用子类的方法和属性, 此时需要将父类类型对象强制转换为子类类型对象;
-- 类型转换方法 : "(类型名称 *) 对象名", 
-- 将父类转为子类 : 这种强转只是改变指针变量的编译时类型, 变量指向的实际类型不会发生改变;
-- 判断类型 : 转换时需要进行类型判断对象类型, 否则容易出错;

(4) 判断指针变量的实际类型

判断指针变量实际类型
-- 判断对象是否是 clazz 类对象 : "- (BOOL) isMemberOfClass : clazz";
-- 判断对象是否是 clazz 类 或 子类 实例 : "- (BOOL) isKindOfClass : clazz :";

-- 判断当前是否是 clazz 子类 : "+ (BOOL) isSubclassOfClass : clazz :"

2. 多态代码示例

代码示例 :

-- OCCatTest.m : 该类与上面的 OCAnimal.h, OCAnimal.m, OCCat.h, OCCat.m 四个类同时使用;

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 五 10/ 2 13:37:16 2015
 ************************************************************************/
#import "OCCat.h"

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		//多态示例
		OCAnimal * animal = [[OCAnimal alloc]init];
		animal.name = @"Jerry";
		animal.age = 18;
		NSLog(@"animal.name : %@, animal.age : %d", animal.name, animal.age);

		OCCat * cat = [[OCCat alloc]init];
		cat.name = @"Tom";
		cat.age = 20;
		NSLog(@"cat.name : %@, cat.age : %d", cat.name, cat.age);

		OCAnimal * father = [[OCCat alloc]init];
		father.name = @"Kit";
		father.age = 25;;
		NSLog(@"father.name : %@, father.age : %d", father.name, father.age);

		//[father purr]; //父类不能调用子类方法, 编译类型与方法不符, 编译不通过

		/*
		 * 类型转换示例
		 * 如果使用 [father purr] 编译时就会报错
		 * 将 father 强转为 OCCat * 类型, 编译运行就没问题了, 如下
		 */
		[(OCCat *)father purr];

		BOOL isAnimalObject = [animal isKindOfClass : [NSObject class]];
		NSLog(@"isAnimalObject : %d", isAnimalObject);
	}
}

-- 执行结果 :

localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCAnimal.m OCCat.m OCCatTest.m
localhost:oc_object octopus$ ./a.out
2015-10-03 07:51:07.415 a.out[712:507] animal.name : Jerry, animal.age : 18
2015-10-03 07:51:07.417 a.out[712:507] cat.name : Tom, cat.age : 20
2015-10-03 07:51:07.417 a.out[712:507] father.name : Kit, father.age : 25
2015-10-03 07:51:07.417 a.out[712:507] Kit 喵喵
2015-10-03 07:51:07.418 a.out[712:507] isAnimalObject : 1

参考文章 :

-- IOS,objective_C中用@interface和 @property 方式声明变量的区别 : 点击打开链接

-- IOS 成员变量,全局变量,局部变量定义,static与extern的区别 : 点击打开链接

-- iOS变量定义在.h还是.m中 : 点击打开链接

--

上一篇:python 实现获取电脑IP、主机名、Mac地址


下一篇:解决"VC6.0的ClassView里不能显示类或成员变量"问题