一. 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中 : 点击打开链接

--

最新文章

  1. Rdeis 详解 小白
  2. atitit.导航的实现最佳实践and声明式编程
  3. 第三集 欠拟合与过拟合的概念、局部加权回归、logistic回归、感知器算法
  4. 对于POI的XSSFCell 类型问题
  5. IPython notebook 使用介绍
  6. 匿名方法和Lambda表达式
  7. Mysql的转义字符
  8. Javascript兑现图片预加载【回调函数,多张图片】 (转载)
  9. Docker环境中部署DzzOffice 1.2.5.2
  10. 仿flash轮播
  11. Alertmanager 集群
  12. 保护代理模式-Access Proxy(Java实现)
  13. C++一个类对象的大小计算
  14. 中国省市县数据库sql文件(2017年10月31日之前)
  15. python---map 用法 [转载]
  16. 背水一战 Windows 10 (48) - 控件(集合类): FlipView
  17. BI 可视化
  18. Linux 下的hiredis的简单安装、测试*(转)
  19. nltk30_Investigating bias with NLTK
  20. nsstring 怎么包含”(引号)号

热门文章

  1. 细胞(cell) 矩阵快速幂
  2. ●BZOJ 4559 [JLoi2016]成绩比较
  3. POJ 3261 可重叠k次最长重复子串
  4. mysql的连接处理过程
  5. C# 枚举在项目中使用心得
  6. PTA 社交网络图中结点的“重要性”计算(30 分)
  7. String 类
  8. Flexible DEMO 实现手淘H5页面的终端适配
  9. RHEL(红帽七)的DNS配置
  10. CodeForces - 766B Mahmoud and a Triangle