一. Objective-C 对象简单处理

1. 包装类

(1) 包装类简介

NSValue 和 NSNumber :

-- 通用包装类 NSValue : NSValue 包装单个 short, int, long, float, char, id, 指针 等数据;

-- NSNumber 包装类 : 用于包装 C 语言数据类型;

NSNumber 方法 :

-- "+ numberWithXxx :" : 将特定类型的值包装成 NSNumber;

-- "- initWithXxx :" : 先创建一个 NSNumber 对象, 再用一个基本类型的值来初始化 NSNumber;

-- "- xxxValue :" : 返回 NSNumber 对象包装的基本类型的值;

(2) 包装类代码示例

代码示例 :

/*************************************************************************
    > File Name: OCNSNumberDemo.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 六 10/ 3 12:50:15 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

int main(int argc, char * argv[])
{
	@autoreleasepool {
		NSNumber * num_int = [NSNumber numberWithInt : 10];
		NSNumber * num_double = [NSNumber numberWithDouble : 10];
		NSNumber * num_char = [NSNumber numberWithChar : 'A'];

		NSLog(@"number_int : %d, number_double : %g, num_char : %c",
			[num_int intValue], [num_double doubleValue], [num_char charValue]);

		NSNumber * num_int1 = [[NSNumber alloc] initWithInt : 10];
		NSNumber * num_double1 = [[NSNumber alloc] initWithDouble : 10];
		NSNumber * num_char1 = [[NSNumber alloc] initWithChar : 'A'];

		NSLog(@"number_int1 : %d, number_double1 : %g, num_char1 : %c",
			[num_int1 intValue], [num_double1 doubleValue], [num_char1 charValue]);
	}
}

-- 执行结果

localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCNSNumberDemo.m
localhost:oc_object octopus$ ./a.out
2015-10-03 13:00:46.465 a.out[887:507] number_int : 10, number_double : 10, num_char : A
2015-10-03 13:00:46.468 a.out[887:507] number_int1 : 10, number_double1 : 10, num_char1 : A

2. description 方法

(1) description 方法简介

description 方法简介 : 类似于 Java 中 Object 的 toString() 方法;

-- 方法来源 : description 是 NSObject 中定义的, 所有的方法都有该方法;

-- 默认方法 : description 默认方法返回 <类名: 地址>;

-- 输出对象 : NSLog() 函数输出一个对象, 其实输出的是该对象的 description 方法;

-- 示例 : OCPerson * person, 打印 [person description] 和 person 输出结果是一样的;

(2) description 示例代码

示例代码 :

/*************************************************************************
    > File Name: OCDescriptionDemo.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 六 10/ 3 14:25:28 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@interface OCDescriptionDemo : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
- (id) initWithNameAndAge : (NSString *) set_name setAge : (int) set_age;
@end

@implementation OCDescriptionDemo
@synthesize name;
@synthesize age;
- (id) initWithNameAndAge : (NSString *) set_name setAge : (int) set_age
{
	self.name = set_name;
	self.age = set_age;
	return self;
}
- (NSString *) description
{
	NSString * des = [NSString stringWithFormat :
		@"<OCDescription[name = %@, age = %d]>", self.name, self.age];
	return des;
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCDescriptionDemo * description = [[OCDescriptionDemo alloc] initWithNameAndAge : @"Tom" setAge : 18];
		NSLog(@"%@", description);
	}
}

-- 执行结果

localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCDescriptionDemo.m
localhost:oc_object octopus$ ./a.out
2015-10-03 14:50:18.665 a.out[970:507] <OCDescription[name = Tom, age = 18]>

3. == 或 isEqual : 方法

(1) "==" 运算符

"==" 简介 :

-- 作用 : 判断两个变量是否相等;

-- 前提 : 两个变量都是基本类型, 两个变量相等返回 true; 指针类型变量比较地址没有任何意义;

(2) 常量池

常量池 :

-- 作用 : 保证相同的字符串常量至右一个, 不能出现多个相同的副本;

-- 例外 : 使用 [NSString stringWithFormat] 方法创建的字符串不会放入常量池;

(3) isEqual 方法

"isEqual" 方法简介 :

-- 来源 : isEqual 方法是 NSObject 类提供的实例方法, 用于判断相同类型的两个变量是否相等;

-- 默认 : 默认方法还是比较地址, 需要开发者重写这个方法;

-- NSString 的 isEqual 方法 : NSString 的 isEqual 方法是判断两个字符串是否相等, 包含的字符串相同就会返回 true;

-- isEqualToString 方法 : 方法 : NSString 中定义的 isEqualToString 方法用于判断当前字符串 与 另一个字符串的字符串序列是否相等;

重写 isEqual 方法标准 :

-- 自反性 : 对象 x, [x isEqual : x] 必须返回 true;

-- 对称性 : 对象 x 和 y, 如果 [x isEqual : y] 返回值 必须与 [y isEqual : x] 返回值相同;

-- 传递性 : 对象 x , y 和 z, [x isEqual : y] = true, [y isEqual : z] = true, 那么 x z 也相等;

-- 一致性 : x , y 对象无论调用多少次, 返回值结果都应该保持一致;

-- nil 对比 : 如果 x 不是 nil, [x isEqual : nil] 必须返回 false;

(4) "==" 和 "isEqual" 示例源码

示例源码 :

/*************************************************************************
    > File Name: OCEqual.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 六 10/ 3 16:07:56 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

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

- (id) initWithName : (NSString *) set_name setAge : (int) set_age;
@end

@implementation OCEqual
@synthesize name;
@synthesize age;

- (id) initWithName : (NSString *) set_name setAge : (int) set_age
{
	self.name = set_name;
	self.age = set_age;
	return self;
}

- (BOOL) isEqual : (id) other
{
	if(self == other)
		return YES;

	if(other != nil && [other isMemberOfClass : OCEqual.class])
	{
		OCEqual * equal = (OCEqual *) other;
		return [self.name isEqual : equal.name] && (self.age == equal.age);
	}
	return NO;
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		int int_a = 10;
		int double_a = 10.0;

		NSLog(@"int_a == double_a : %d", (int_a == double_a));

		NSString * str_a = @"Octopus";
		NSString * str_b = @"Octopus";
		NSString * str_c = [NSString stringWithFormat : @"Octopus"];
		NSString * str_d = [NSString stringWithFormat : @"Octopus"];

		NSLog(@"str_a == str_b : %d, str_c == str_d : %d, [str_c isEqual : str_d] : %d",
			str_a == str_b, str_c == str_d, [str_c isEqual : str_d]);

		OCEqual * equal_a = [[OCEqual alloc] initWithName : @"Tom" setAge : 18];
		OCEqual * equal_b = [[OCEqual alloc] initWithName : @"Jerry" setAge : 20];
		OCEqual * equal_c = [[OCEqual alloc] initWithName : @"Jerry" setAge : 20];

		NSLog(@"[equal_a isEqual : equal_b] : %d, [equal_b isEqual : equal_c] : %d",
			[equal_a isEqual : equal_b], [equal_b isEqual : equal_c]);
	}
}

-- 执行结果

localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCEqual.m
localhost:oc_object octopus$ ./a.out
2015-10-03 16:58:35.690 a.out[1168:507] int_a == double_a : 1
2015-10-03 16:58:35.693 a.out[1168:507] str_a == str_b : 1, str_c == str_d : 0, [str_c isEqual : str_d] : 1
2015-10-03 16:58:35.693 a.out[1168:507] [equal_a isEqual : equal_b] : 0, [equal_b isEqual : equal_c] : 1

二. 类别 与 扩展

1. Category 类别

(1) 扩展类簇需求

类簇扩展需求 : 开发过程中有时需要扩展类行为;

-- 继承 : 通过继承, 子类在父类基础上添加方法 或者 重写父类方法;

-- 问题 : 如果想要为父类增加一个方法, 子类同时也继承这些方法, 此时使用继承就满足不了这个功能了;

-- 类簇 : OC 中没有接口, 需要接口时, 就会选择定义一个父类, 以该父类派生 N 个子类, 该系列的类被成为 类簇;

-- 类簇扩展方法 : 为父类增加方法, 类簇中得子类同时也增加该方法, 扩展类簇中得父类是最合适的方法;

(2) Category 类别

类别 (category) 简介 :

-- 作用 : 为现有类添加方法, 不需要访问原有类代码, 不需要继承;

-- 有点 : 动态地为现有类添加方法, 将类定义模块化 分布到多个文件中;

(3) Category 类别 接口 语法格式

类别 (category) 接口部分语法格式 :

-- 接口文件类命名 : "类名+类别名.h", 如 要扩展 OCPerson 类, 类别名为 SB, 那么接口文件名就是 "OCPerson+SB.h";

-- 示例 :

@interface 已有类 (类别名)
//方法定义
...
@end

-- 类别名 : 必须是项目中没有的类, 定义类别时使用的类名, 必须是已有的类;

-- 圆括号 : 类别名 定义在 需要扩展的已有类之后, 必须使用圆括号括起来;

-- 定义内容 : 类别中一般情况下只定义方法;

(4) Category 类别 实现类 语法格式

类别 (category) 实现部分语法格式 :

-- 实现类文件命名 : "类名+类别名.m", 如 要扩展 OCPerson 类, 类别名为 SB, 那么接口文件名就是 "OCPerson+SB.m";

-- 示例 :

@implementation 已有类 (类别名)
//方法定义
...
@end

(5) Category 类别 注意点

注意事项 :

-- 影响范围 : 通过 category 添加新方法后, 会影响到 指定的被扩展的类, 同时也会影响到其子类;

-- 多个类别 : 一个类可以 对应多个类别, 这些类别都可以为类增加方法定义;

-- 类别优点 : 进行模块化设计, 调用私有方法, 实现非正式协议;

(6) Category 扩展 NSNumber 示例

NSNumber 扩展示例 : 为其添加一个计算圆面积的方法;

-- NSNumber+SB.h :

/*************************************************************************
    > File Name: NSNumber+SB.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 六 10/ 3 18:58:53 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@interface NSNumber (SB)
- (NSNumber *) circleAera : (double) radius;
@end

-- NSNumber+SB.m

/*************************************************************************
    > File Name: NSNumber+SB.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 六 10/ 3 19:02:05 2015
 ************************************************************************/
 #import "NSNumber+SB.h"
 @implementation NSNumber (SB)

 - (NSNumber *) circleAera : (double) radius
 {
	double aera = 3.1415926 * radius * radius;
	return [NSNumber numberWithDouble : aera];
 }

 @end

-- NSNumber+SBTest.m

/*************************************************************************
    > File Name: NSNumber+SBTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 六 10/ 3 19:08:19 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
#import "NSNumber+SB.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		NSNumber * num = [NSNumber numberWithInt : 3];
		NSNumber * circleAera = [num circleAera : 1];
		NSLog(@"%@", circleAera);
	}
}

-- 执行结果

localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation NSNumber+SB.m NSNumber+SBTest.m
localhost:oc_object octopus$ ./a.out
2015-10-03 19:18:13.625 a.out[1333:507] 3.1415926

2. Category 类别实际用法

(1) 类的模块化设计

模块化设计简介 :

-- 实现部分唯一 : 定义一个类是, 使用 "类名.h" 定义接口部分, 使用 "类名.m" 定义实现部分, 不能将实现部分定义在多个 ".m" 后缀 文件中;

-- 文件臃肿 : 如果类很大, 将所有的代码放在一个 "类名.m" 文件中, 非常难维护;

(2) 调用私有方法

私有方法调用简介 :

-- 私有方法 : 接口中没有定义, 在实现部分定义的方法是 私有方法, 不允许被外部调用;

-- 调用私有方法一 : 使用 NSObject 的 "performSelector :"执行调用, 也是可以调用私有方法的, 不过此方法会避开语法检查, 导致未知问题;

(3) 调用私有方法 代码示例

代码示例 :

-- OCPrivate.h :

/*************************************************************************
    > File Name: OCPrivate.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 06:55:34 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@interface OCPrivate : NSObject
@property (nonatomic, copy) NSString * name;
-(void) info;
@end

-- OCPrivate.m :

/*************************************************************************
    > File Name: OCPrivate.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 06:57:48 2015
 ************************************************************************/
#import "OCPrivate.h"

@implementation OCPrivate
@synthesize name;

- (void) info
{
	NSLog(@"name : %@", self.name);
}

- (void) speak
{
	NSLog(@"Hello World !");
}
@end

-- OCPrivate+SB.h :

/*************************************************************************
    > File Name: OCPrivate+SB.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 07:19:35 2015
 ************************************************************************/
#import "OCPrivate.h"
@interface OCPrivate (SB)
- (void) speak;
@end

-- OCPrivateTest.m :

/*************************************************************************
    > File Name: OCPrivateTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 07:22:04 2015
 ************************************************************************/
#import "OCPrivate+SB.h"

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPrivate * priva = [[OCPrivate alloc] init];
		priva.name = @"Tom";
		[priva info];
		[priva speak];

	}
}

3. extension 扩展

(1) extension 简介

extension 简介 :

-- 作用 : 扩展相当于匿名类别;

-- 语法 :

@interface 已有类 ()
{
	//实例变量 ...
}
// 方法定义 ...
@end

-- 用法 : 定义两个头文件, OCExtension.h 和 OCExtension+speak.h, OCExtension.m 导入 OCExtension+speak.h 头文件;

(2) extension 源码示例

源码示例 :

-- OCExtension.h :

/*************************************************************************
    > File Name: OCExtension.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 08:18:43 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@interface OCExtension : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age; 

- (void) info;
@end

-- OCExtension+speak.h :

/*************************************************************************
    > File Name: OCExtension+speak.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 08:31:37 2015
 ************************************************************************/
#import "OCExtension.h"
@interface OCExtension ()
@property (nonatomic, copy) NSString * home;
- (void) speak : (NSString *) content;
@end

-- OCExtension.m :

/*************************************************************************
    > File Name: OCExtension.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 08:27:50 2015
 ************************************************************************/
#import "OCExtension+speak.h"
@implementation OCExtension
@synthesize name;
@synthesize age;
@synthesize home;
- (void) info
{
	NSLog(@"info : name : %@ , age : %d", self.name, self.age);
}
- (void) speak : (NSString *) content
{
	NSLog(@"%@ speak %@", self.name, content);
}
@end

-- OCExtensionTest.m :

/*************************************************************************
    > File Name: OCExtensionTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 日 10/ 4 13:20:42 2015
 ************************************************************************/
#import "OCExtension+speak.h"

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

		[extension info];
		[extension speak : @"Are you fucking kidding me"];
	}
}

三. 协议 与 委托

1. 类别实现非正式协议

(1) 非正式协议简介

协议简介 :

-- 作用 : OC 中得协议作用相当于其它语言中得接口;

-- 协议表现 : 协议定义的是 多个类 共同的行为规范, 通常定义一组公用方法, 这些方法都没有实现, 方法由类来实现;

非正式协议简介 :

-- 创建 NSObject 类别 : 以 NSObject 为基础, 为 NSObject 创建类别, 为该类别指定新增方法, 即给所有的 NSObject 子类增加了新方法;

-- 实现 NSObject 类别 : 实现 NSObject 类别时, 实现该列别下地所有方法, 即之前在 NSObject 类别中定义的方法;

(2) 非正式协议代码示例

非正式协议代码示例 :

-- NSObject+speak.h : 为 NSObject 定义的类别接口, 所有的继承 NSObject 的类都必须实现该类别中得抽象方法;

/*************************************************************************
    > File Name: NSObject+speak.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 08:55:48 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@interface NSObject (speak)
- (void) speak;
@end

-- OCNSObjectProtocal.h : NSObject 子类接口, 该接口继承 NSObject 类, 注意 需要导入 NSObject+speak.h 头文件;

/*************************************************************************
    > File Name: OCNSObjectProtocal.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:00:37 2015
 ************************************************************************/
#import "NSObject+speak.h"
@interface OCNSObjectProtocal : NSObject
@end

-- OCNSObjectProtocal.m : OCNSObjectProtocal 实现类, 在该实现类中必须实现 类别中定义的 speak 方法;

/*************************************************************************
    > File Name: OCNSObjectProtocal.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:02:03 2015
 ************************************************************************/
#import "OCNSObjectProtocal.h"
@implementation OCNSObjectProtocal
- (void) speak
{
	NSLog(@"Speak Hello World");
}
@end

-- OCNSObjectProtocalTest.m : 测试类, 测试以上代码是否可以执行;

/*************************************************************************
    > File Name: OCNSObjectProtocalTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:04:31 2015
 ************************************************************************/
#import "OCNSObjectProtocal.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCNSObjectProtocal * obj = [[OCNSObjectProtocal alloc] init];
		[obj speak];
	}
}

-- 执行结果 :

bogon:oc_object octopus$ clang -fobjc-arc -framework Foundation OCNSObjectProtocal.m OCNSObjectProtocalTest.m
bogon:oc_object octopus$ ./a.out
2015-10-05 09:06:44.895 a.out[2100:507] Speak Hello World

2. 定义正式协议

(1) 正式协议语法

正式协议语法 :

-- 语法 :

@protocol 协议名称 <父类协议1, 父类协议2 ...>
// N 个协议方法
@end

-- 协议名称规范 : 采用与类名相同的命名规则;

-- 继承规则 : 一个协议 可以有 多个父类协议, 协议只能继承协议, 不能继承类;

-- 方法规则 : 协议中只能定义抽象方法, 不能定义方法实现, 既可以定义类方法, 也可以定义实例方法;

(2) 实现协议

实现协议语法 :

-- 语法 :

@interface 类名 : 父类 <协议1, 协议2...>

-- 对应关系 : 一个类可以实现多个协议;

(3) 声明协议变量

变量声明 :

-- 使用原变量声明 : "变量名 * 对象名" , 如 "OCCat * cat";

-- 使用协议定义 : "NSObject <协议1, 协议2 ...> * 对象名", 如 "NSObject<OCProtocolCat> * cat";

-- 使用 id 类型定义 : "id<OCProtocolCat> 对象名", 如 "id<OCProtocolCat> cat", 注意此处没有指针标识;

(4) 正式协议实现代码

代码示例 :

-- OCAnimalProtocol.h : 最基础的协议1;

/*************************************************************************
    > File Name: OCAnimalProtocol.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:25:41 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@protocol OCAnimalProtocol
- (void) name;
- (void) age;
@end

-- OCProtocolBord.h : 最基础的协议2;

/*************************************************************************
    > File Name: OCProtocolBord.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:40:08 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
@protocol OCProtocolBord
- (void) fly;
@end

-- OCProtocolCat.h : 该协议实现了 上面的两个协议;

/*************************************************************************
    > File Name: OCProtocolCat.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:45:17 2015
 ************************************************************************/

#import <Foundation/Foundation.h>
#import "OCAnimalProtocol.h"
#import "OCProtocolBord.h"

@protocol OCProtocolCat <OCAnimalProtocol, OCProtocolBord>
- (void) purr;
@end

-- OCCat.h : OCCat 类接口部分, 生命了该类 实现协议 OCProtocolCat 协议;

/*************************************************************************
    > File Name: OCCat.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:50:21 2015
 ************************************************************************/

#import "OCProtocolCat.h"

@interface OCCat : NSObject <OCProtocolCat>
@end

-- OCCat.m : OCCat 类实现部分;

/*************************************************************************
    > File Name: OCCat.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 09:52:45 2015
 ************************************************************************/

 #import "OCCat.h"

 @implementation OCCat
 - (void) name
 {
	NSLog(@"name : cat");
 }
 - (void) age
 {
	NSLog(@"age : 18");
 }
 - (void) fly
 {
	NSLog(@"cat fly");
 }
 - (void) purr
 {
	NSLog(@"cat purr");
 }
 @end

-- OCProtocolTest.m : 以上类的功能测试类;

/*************************************************************************
    > File Name: OCProtocolTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 10:14:38 2015
 ************************************************************************/

#import <Foundation/Foundation.h>
#import "OCCat.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCCat * cat = [[OCCat alloc] init];
		[cat name];
		[cat age];
		[cat fly];
		[cat purr];

		NSObject<OCProtocolCat> * cat1 = [[OCCat alloc] init];
		[cat1 name];
		[cat1 age];
		[cat1 fly];
		[cat1 purr];

		id<OCAnimalProtocol> cat2 = [[OCCat alloc] init];
		[cat2 name];
		[cat2 age];
	}
}

-- 执行结果 :

bogon:6.4 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCProtocolTest.m
bogon:6.4 octopus$ ./a.out
2015-10-05 10:24:20.099 a.out[2271:507] name : cat
2015-10-05 10:24:20.101 a.out[2271:507] age : 18
2015-10-05 10:24:20.102 a.out[2271:507] cat fly
2015-10-05 10:24:20.102 a.out[2271:507] cat purr
2015-10-05 10:24:20.102 a.out[2271:507] name : cat
2015-10-05 10:24:20.103 a.out[2271:507] age : 18
2015-10-05 10:24:20.103 a.out[2271:507] cat fly
2015-10-05 10:24:20.104 a.out[2271:507] cat purr
2015-10-05 10:24:20.104 a.out[2271:507] name : cat
2015-10-05 10:24:20.104 a.out[2271:507] age : 18

3. 委托

委托概念 : 定义协议的类定义协议的方法 委托给 实现协议的类;

-- 好处 : 类具有更好地通用性, 具体的动作交给实现类完成;

创建工程 :

-- 欢迎界面, 选择 Create a new xcode project;

-- 创建一个 OS 下地 Cocoa Application :

-- 创建 工程 :

项目中得源文件 :

-- main.m : main() 函数入口;

-- OCAppDelegate.h : OCAppDelegate 类接口文件;

-- OCAppDelegate.m : OCAppDelegate 类实现部分;

代码示例 :

-- 前置操作 : 删除 MainMenu.xib 文件, 删除 Hello-Info.plist 中的 MainMenu 选项;

-- OCAppDelegate.h :

//
//  OCAppDelegate.h
//  Hello
//
//  Created by octopus on 15-10-5.
//  Copyright (c) 2015年 www.octopus.org.cn. All rights reserved.
//

#import <Cocoa/Cocoa.h>

//该接口 实现 NSApplicationDelegate 协议
@interface OCAppDelegate : NSObject <NSApplicationDelegate>
//定义窗口
@property (strong) NSWindow *window;

@end

-- OCAppDelegate.m :

//
//  OCAppDelegate.m
//  Hello
//
//  Created by octopus on 15-10-5.
//  Copyright (c) 2015年 www.octopus.org.cn. All rights reserved.
//

#import "OCAppDelegate.h"

@implementation OCAppDelegate
@synthesize window;

//应用加载完成时回调的方法
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
    //设置窗口属性
    self.window = [[NSWindow alloc] initWithContentRect :
                   NSMakeRect(300, 300, 320, 200)
                   styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |NSClosableWindowMask)
                   backing:NSBackingStoreBuffered
                   defer:NO];
    self.window.title = @"Hello World";

    //设置文本框属性
    NSTextField * label = [[NSTextField alloc] initWithFrame:NSMakeRect(60, 120, 200, 60)];
    [label setSelectable:YES];
    [label setBezeled:YES];
    [label setDrawsBackground:YES];
    [label setStringValue:@"HELLO WORLD"];

    //设置按钮属性
    NSButton * button = [[NSButton alloc] initWithFrame:NSMakeRect(120, 40, 80, 30)];
    button.title = @"OCTOPUS";
    [button setBezelStyle:NSRoundedBezelStyle];
    [button setBounds:NSMakeRect(120, 40, 80, 30)];

    //将 文本框 和 按钮 添加到窗口中
    [self.window.contentView addSubview:label];
    [self.window.contentView addSubview:button];
}

//加载完成时回调的方法
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
    //显示窗口
    [self.window makeKeyAndOrderFront:self];
}

@end

-- main.m :

//
//  main.m
//  Hello
//
//  Created by octopus on 15-10-5.
//  Copyright (c) 2015年 www.octopus.org.cn. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "OCAppDelegate.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        //创建一个实现了 NSApplicationDelegate 协议的对象
        OCAppDelegate * delegate = [[OCAppDelegate alloc] init];
        //获取 NSApplication 单例对象
        [NSApplication sharedApplication];
        //设置代理, 将处理方法委托给 delegate
        [NSApp setDelegate : delegate];
        //开始运行程序
        return NSApplicationMain(argc, argv);
    }

}

-- 运行结果 :

四. 异常处理

1. @try ... @catch ... @finally ... 异常捕捉

(1) Objective-C 异常机制

Objective-C 异常机制 :

-- 作用 : 开发者将引发异常的代码放在 @try 代码块中, 程序出现异常 使用 @catch 代码块进行捕捉;

-- 每个代码块作用 : @try 代码块存放可能出现异常的代码, @catch 代码块 异常处理逻辑, @finally 代码块回收资源;

-- 语法示例 :

@try
{
	// 业务逻辑
}
@catch (异常类型名1 ex)
{
	//异常处理代码
}
@catch (异常类型名2 ex)
{
	//异常处理代码
}
// 可以捕捉 N 个 异常 ...
@finally
{
	//回收资源
}

(2) Objective-C 异常处理过程

异常处理过程 :

-- 生成异常对象 : @try 中出现异常, 系统会生成一个异常对象, 该对象提交到系统中 系统就会抛出异常;

-- 异常处理流程 : 运行环境接收到 异常对象时, 如果存在能处理该异常对象的 @catch 代码块, 就将该异常对象交给 @catch 处理, 该过程就是捕获异常, 如果没有 @catch 代码块处理异常, 程序就会终止;

-- @catch 代码块捕获过程 : 运行环境接收到 异常对象 时, 会依次判断该异常对象类型是否是 @catch 代码块中异常或其子类实例, 如果匹配成功, 被匹配的 @catch 就会处理该异常, 都则就会跟下一个 @catch 代码块对比;

-- @catch 处理异常 : 系统将异常对象传递给 @catch 形参, @catch 通过该形参获取异常对象详细信息;

其它注意点 :

-- @try 与 @catch 对应关系 : 一个 @try 代码块 可以对应 多个 @catch 代码块;

-- {} 省略问题 : 异常捕获的 @try @catch @finally 的花括号不可省略;

NSException 异常类 :

-- 简介 : NSException 是 OC 中所有异常的父类;

-- 位置永远在最后 : @catch 代码块捕获异常时查看 异常对象类型是否是 捕获的异常类型 或者其子类, 一旦放在开头, 后面的异常永远不可能捕获;

(3) 异常信息访问

异常信息访问 :

-- name : 返回异常的详细名称;

-- reason : 返回异常引发的原因;

-- userInfo : 返回异常的用户信息, 一个 NSDictionary 对象;

(4) 使用 finally 回收资源

回收物理资源 : @try 代码块中打开物理资源, 数据库 网络连接 文件等, 都需要回收, 在 @finally 中回收最好;

-- 回收位置分析 : 如果再 @try 中回收, 出现异常, 异常后面的代码无法执行, @catch 中回收, 如果不出现异常, 该代码块就不会执行; 因此 finally 中是必执行的代码, 在这里回收最合适;

(5) 异常代码示例

异常代码示例 :

-- OCAnimal.h : 定义协议;

/*************************************************************************
    > File Name: OCAnimal.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:30:02 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@protocol OCAnimal
@optional
- (void) run;
@end

-- OCCat.h : 定义 OCCat 接口;

/*************************************************************************
    > File Name: OCCat.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:33:59 2015
 ************************************************************************/
#import "OCAnimal.h"

@interface OCCat : NSObject <OCAnimal>
@end

-- OCCat.m : 定义 OCCat 实现类;

/*************************************************************************
    > File Name: OCCat.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:36:36 2015
 ************************************************************************/
#import "OCCat.h"

@implementation OCCat
@end

-- OCCatTest.m : 测试类;

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:38:01 2015
 ************************************************************************/
#import "OCCat.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCCat * cat = [[OCCat alloc] init];
		[cat run];
	}
}

-- 执行结果 :

bogon:6.5 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCCatTest.m
bogon:6.5 octopus$ ./a.out
2015-10-05 16:39:23.589 a.out[2985:507] -[OCCat run]: unrecognized selector sent to instance 0x7fd7a3401870
2015-10-05 16:39:23.611 a.out[2985:507] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[OCCat run]: unrecognized selector sent to instance 0x7fd7a3401870'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff903dd25c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8ecdbe75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff903e012d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
	3   CoreFoundation                      0x00007fff9033b044 ___forwarding___ + 452
	4   CoreFoundation                      0x00007fff9033adf8 _CF_forwarding_prep_0 + 120
	5   a.out                               0x0000000108575efd main + 109
	6   libdyld.dylib                       0x00007fff851f35fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

(6) 异常捕获代码示例

异常捕获取示例 : 该示例扔使用上面的 OCAnimal.h, OCCat.h, OCCat.m 示例;

-- OCCatTest.m :

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:38:01 2015
 ************************************************************************/
#import "OCCat.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		@try
		{
			OCCat * cat = [[OCCat alloc] init];
			[cat run];
		}
		@catch (NSException * ex)
		{
			NSLog(@"exception name : %@, reason : %@", ex.name, ex.reason);
		}
		@finally
		{
			NSLog(@"finally execute");
		}
		NSLog(@"success");
	}
}

-- 执行结果 :

bogon:6.5 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCCatTest.m
bogon:6.5 octopus$ ./a.out
2015-10-05 16:53:46.850 a.out[3008:507] -[OCCat run]: unrecognized selector sent to instance 0x7f884bc018b0
2015-10-05 16:53:46.853 a.out[3008:507] exception name : NSInvalidArgumentException, reason : -[OCCat run]: unrecognized selector sent to instance 0x7f884bc018b0
2015-10-05 16:53:46.853 a.out[3008:507] finally execute
2015-10-05 16:53:46.854 a.out[3008:507] success

2. 抛出自定义异常

(1) 自定义异常语法

自定义异常抛出 :

-- 语法 :

@throw 异常对象;

(2) 自定义异常代码示例

自定义异常代码示例 :

-- OCException.h 接口 :

/*************************************************************************
    > File Name: OCException.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:58:24 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@interface OCException : NSException
@end

-- OCException.m 实现类 :

/*************************************************************************
    > File Name: OCException.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 17:03:15 2015
 ************************************************************************/
#import "OCException.h"

@implementation OCException
@end

-- OCCatTest.m 测试类 :

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 16:38:01 2015
 ************************************************************************/
#import "OCException.h"

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		@throw [[OCException alloc]
			initWithName : @"OCException"
			reason : @"this reason is imporant"
			userInfo : nil];
	}
}

-- 执行结果 :

bogon:6.5 octopus$ clang -fobjc-arc -framework Foundation OCException.m OCCatTest.m
bogon:6.5 octopus$ ./a.out
2015-10-05 17:04:12.432 a.out[3040:507] *** Terminating app due to uncaught exception 'OCException', reason: 'this reason is imporant'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff903dd25c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8ecdbe75 objc_exception_throw + 43
	2   a.out                               0x00000001062abef7 main + 135
	3   libdyld.dylib                       0x00007fff851f35fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type OCException
Abort trap: 6

五. Objective-C 反射

1. 获取 Class

(1) 程序 与 环境 交互方式

程序 与 运行环境交互方式 :

-- 通过 OC 源码 : 编写 OC 源码, 编译器编译, 运行在运行环境中;

-- 通过 NSObject 动态编程 : NSObject 是所有类的基类, 所有对象都可以直接调用 NSObject 方法;

-- 调用 运行时函数 动态编程 : 运行时系统是动态库, 可以直接调用这些动态共享库;

(2) 获取 Class 方式

获取 Class 方式 :

-- 通过类名 : 使用 "Class NSClassFromString (NSString * aClassName)" 函数获取 Class 对象, 传入 类名 字符串;

-- class 类方法 : 调用类方法 class, 调用方式 [NSString class];

-- class 对象方法 : 调用对象的 class 方法, 调用方式 [@"hello" class];

-- 推荐使用第二种方式 : 代码更安全, 编译阶段就可以检查 Class 是否存在, 程序性能高;

(3) 获取 Class 代码示例

代码示例 :

/*************************************************************************
    > File Name: OCGetClass.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 23:31:51 2015
 ************************************************************************/

#import <Foundation/Foundation.h>

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//通过 NSClassFromString 方法传入 类名字符串 获取 Class 对象
		Class clazz = NSClassFromString(@"NSDate");
		NSLog(@"%@", clazz);
		id date = [[clazz alloc] init];
		NSLog(@"date : %@", date);

		NSString * str = @"hello";
		// 通过调用 类 或 对象的 class 方法
		NSLog(@"[str class] : %@, [NSString class] : %@", [str class], [NSString class]);
		// 通过调用 类 或 对象的 getter 方法获取, 即用 . 方法获取
		NSLog(@"str.class : %@, NSString.class : %@", str.class, NSString.class);

	}
}

-- 执行结果 :

bogon:6.6 octopus$ clang -fobjc-arc -framework Foundation OCGetClass.m
bogon:6.6 octopus$ ./a.out
2015-10-05 23:39:28.692 a.out[3237:507] NSDate
2015-10-05 23:39:28.699 a.out[3237:507] date : 2015-10-05 15:39:28 +0000
2015-10-05 23:39:28.700 a.out[3237:507] [str class] : __NSCFConstantString, [NSString class] : NSString
2015-10-05 23:39:28.700 a.out[3237:507] str.class : __NSCFConstantString, NSString.class : NSString

2. 检查继承关系

(1) 继承关系判断

继承关系判断方法 :

-- 判断类 : isMemberOfClass 方法, 传入 Class 对象, 判断该对象是否是 Class 对象对应类的实例;

-- 判断类或子类 : isKindOfClass 方法, 传入 Class 对象, 判断该对象是否是 Class 对象对应类 或 子类的实例;

-- 判断协议 : conformsToProtocol 犯法, 传入 Protocol 参数, 传入方法 "@protocol(协议名称)" 或者  "Protocol * NSProtocolFromString(NSString * @"协议名称")" 两种方法获取协议参数;

(2) 继承关系判断代码示例

源码示例 :

-- OCAnimal.h : 定义协议;

/*************************************************************************
    > File Name: OCAnimal.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 23:54:14 2015
 ************************************************************************/
#import <Foundation/Foundation.h>

@protocol OCAnimal
- (void) name;
@end

-- OCCat.h : 定义接口;

/*************************************************************************
    > File Name: OCCat.h
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 23:56:16 2015
 ************************************************************************/
#import "OCAnimal.h"

@interface OCCat : NSObject <OCAnimal>
@end

-- OCCat.m : 定义实现类;

/*************************************************************************
    > File Name: OCCat.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一 10/ 5 23:58:47 2015
 ************************************************************************/
#import "OCCat.h"

@implementation OCCat
- (void) name
{
	NSLog(@"My name is Tom");
}
@end

-- OCCatMain.m : 测试类;

/*************************************************************************
    > File Name: OCCatMain.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 二 10/ 6 00:00:01 2015
 ************************************************************************/
#import "OCCat.h"

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCCat * cat = [[OCCat alloc] init];
		NSLog(@"%@", cat.class);

		NSLog(@"cat isMemberOfClass OCCat : %d", [cat isMemberOfClass : OCCat.class]);
		NSLog(@"cat isMemberOfClass NSObject : %d", [cat isMemberOfClass : NSObject.class]);
		NSLog(@"cat isKindOfClass OCCat : %d", [cat isKindOfClass : OCCat.class]);
		NSLog(@"cat isKindOfClass OCCat : %d", [cat isKindOfClass : NSObject.class]);
		NSLog(@"cat conformsToProtocol OCAnimal : %d", [cat conformsToProtocol : @protocol(OCAnimal)]);
	}
}

-- 执行结果 :

bogon:6.6 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCCatMain.m
bogon:6.6 octopus$ ./a.out
2015-10-06 00:07:56.838 a.out[3337:507] OCCat
2015-10-06 00:07:56.840 a.out[3337:507] cat isMemberOfClass OCCat : 1
2015-10-06 00:07:56.840 a.out[3337:507] cat isMemberOfClass NSObject : 0
2015-10-06 00:07:56.841 a.out[3337:507] cat isKindOfClass OCCat : 1
2015-10-06 00:07:56.841 a.out[3337:507] cat isKindOfClass OCCat : 1
2015-10-06 00:07:56.842 a.out[3337:507] cat conformsToProtocol OCAnimal : 1

3. 动态调用方法

(1) 动态调用成员变量

KVC 机制 : 通过该机制可以动态调用对象的 getter 和 setter 方法, 不论 该变量定义位置 (接口 | 实现) 和 使用何种访问控制符 (private | public), 都可以使用 KVC 访问;

(2) 判断方法是否可调用

判断对象是否可以调用方法 : NSObject 中定义了 respondsToSelector : 方法, 该方法传入 SEL 参数, 该参数代表方法, 如果可以调用 返回 YES, 反之 返回 NO;

获取 SEL 对象方法 :

-- 指令获取 : 使用 @selector 指令获取当前类中指定的方法, 参数是 完整的方法签名关键字, 只有方法名不够;

@selector(setAge:) withObject

-- 方法获取 : 使用 SEL NSSelectorFromString(NSString * aSelectorName) 函数, 根据方法签名关键字字符串获取对应方法;

NSSelectorFromString(@"setAge:")

(3) SEL 动态调用方法

动态调用对象方法 :

-- 动态调用一 : NSObject 的 performSelector : 方法可以调用方法, 需要传入 SEL 对象, 传入参数 可以通过 withObject: 标签传入参数;

[student performSelector : @selector(setAge:) withObject : [NSNumber numberWithInt : 18]];
[student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];

-- 动态调用二 : objc_msgSend(receiver, selector, ...) 函数调用方法, 参数一 方法调用者, 参数二 调用的方法, 剩余参数 方法参数;

objc_msgSend (student, @selector(setAge:), [NSNumber numberWithInt : 20]);
objc_msgSend (student, NSSelectorFromString(@"setAge:"), [NSNumber numberWithInt : 21]);

-- 注意 : 使用第二种方法需要导入包 "#import <objc/message.h>", 返回浮点数时调用 objc_msgSend_fpret(), 返回结构体数据时 使用 objc_msgSend_stret() 函数;

(4) IMP 动态调用方法

IMP 动态调用方法 简介 :

-- 获取 IMP 对象 : NSObject 定义了 "- (IMP) methodForSelector : (SEL) aSelector :" 方法, 该方法传入 SEL 参数, 返回 IMP 对象;

-- IMP 作用 : IMP 是 OC 方法函数指针变量, 代表函数入口, 通过 IMP 也可以调用函数;

-- IMP 调用方法语法 : "返回值类型 (*指针变量) (id, SEL, ...)" , 参数一 方法调用者, 参数二 方法 SEL 对象, 后面参数是方法参数;

(5) 动态调用方法 示例代码

示例代码 :

-- OCStudent.m : 接口实现类主函数一体;

/*************************************************************************
    > File Name: OCStudent.m
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 二 10/ 6 11:19:49 2015
 ************************************************************************/
#import <Foundation/Foundation.h>
#import <objc/message.h>

@interface OCStudent : NSObject
@end

@implementation OCStudent

- (void) setAge : (NSNumber *) age
{
	int age_num = [age intValue];
	NSLog(@"age is : %d", age_num);
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCStudent * student = [[OCStudent alloc] init];

		[student performSelector : @selector(setAge:) withObject : [NSNumber numberWithInt : 18]];
		[student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];

		objc_msgSend (student, @selector(setAge:), [NSNumber numberWithInt : 20]);
		objc_msgSend (student, NSSelectorFromString(@"setAge:"), [NSNumber numberWithInt : 21]);
	}
}

-- 执行结果 : 有报警;

bogon:6.6 octopus$ clang -fobjc-arc -framework Foundation OCStudent.m
OCStudent.m:29:12: warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks]
                [student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];
                         ^
OCStudent.m:29:30: note: used here
                [student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];
                                           ^
1 warning generated.
bogon:6.6 octopus$ ./a.out
2015-10-06 12:00:41.669 a.out[747:507] age is : 18
2015-10-06 12:00:41.671 a.out[747:507] age is : 19
2015-10-06 12:00:41.671 a.out[747:507] age is : 20
2015-10-06 12:00:41.672 a.out[747:507] age is : 21

最新文章

  1. oracle 修改字符集支持中文
  2. C# CRC-CCITT (Kermit)校验方法
  3. [ZigBee] 10、ZigBee之睡眠定时器
  4. JavaScript学习笔记——事件
  5. 批处理快速创建wifi
  6. IOS证书的申请和使用
  7. java作业4
  8. APKTool 提取APK文件的资源
  9. 随机森林分类(Random Forest Classification)
  10. snowflake算法(java版)
  11. AngularJS开发下一代Web应用笔记(一)
  12. 使用EF实现数据库的增删改查
  13. UDP协议详解
  14. jQuery 操作属性
  15. 集合Collection总览
  16. jbpm - 工作流的基本操作
  17. echarts、higncharts折线图或柱状图显示数据为0的点
  18. 关于SVD
  19. 十图详解tensorflow数据读取机制(附代码)转知乎
  20. 01:open-falcon入门篇

热门文章

  1. 【ZOJ 3609】Modular Inverse 最小乘法逆元
  2. 【Aho-Corasick automation 大米饼模板】
  3. [APIO2014]
  4. python3 条件判断,循环,三元表达式
  5. 在 TensorFlow 中实现文本分类的卷积神经网络
  6. 基于FPGA的数字识别的实现
  7. 触发事件trigger
  8. You And Me 不见不散!
  9. Redis持久化的两种方式(RDB和AOF)
  10. php中AJAX请求中使用post和get请求的区别