【Objective-C】3 封装
第三节 封装
01 异常处理
- 错误
一般情况下,是指源代码不符合语法规范,出现编译报错,使程序无法编译 - bug
程序可以编译 链接 执行,但结果与预期不符 ---> 通过调试寻找产生 bug 的原因 - 异常
程序可以链接 编译 执行,但在处于某种特定状态下,程序会终止运行(崩溃),不会执行后面的代码
处理异常的目的:为了让程序执行发生异常时不崩溃,而是继续向下执行
语法:
@try
{
// 将有可能发生异常的代码放入 @try{} 中
// 当这段代码执行时,若发生异常,之前从当前位置跳转到 @catch,执行其 {} 中的代码
// 若未发生异常,则略过 @catch 部分
}
@catch(NSException *ex){
// 一般 @catch 中的代码用来处理 / 记录异常
// 可以通过 %@ 打印 catch 的参数 ex 指向的对象的值,即发生异常的原因
}
// @try{ } @catch{ } 执行结束后,会继续执行后面的代码
注意:
-
可以在 @catch{ } 后面接一个 @finally{ } ,其中的代码无论是否发生异常都会被执行
-
该方法不是万能的,不是所有运行时的异常都会处理(比如,无法处理 C 语言的异常)
-
实际开发过程中,该方法使用较少 ---> 避免异常最常用的方法还是逻辑判断(if 语句)
// 不使用上述方法如何处理 / 避免异常? // 例:通过以下方式,先判断类中是否存在 sayHi 方法,再调用 Person* p1 = [Person new]; if([p1 respondsToSelector:@selector(sayHi)]){ [p1 sayHi]; }
02 类方法
-
OC 中的方法有两种:
- 对象方法 / 实例方法:如果想调用方法必须先创建对象,再使用对象名来调用
- 类方法:不依赖于对象,直接通过类名来调用
// 声明: @interface Person : NSObject{ NSString* _name; int age; } - (void)sayHi; // 对象方法,以减号 - 开头 + (void)sayBye; // 类方法,以加号 + 开头 @end @implementation Person - (void)sayHi{ NSLog(@"你好,对象方法"); } + (void)sayBye{ NSLog(@"再见,类方法"); } @end // 调用 Person* p1 = [Person new]; [p1 sayHi]; // [对象名 方法名] [Person sayBye]; // [类名 类方法]
类方法特点:
-
不需要创建对象,节约空间;直接找到类 - 类中的方法,提高效率
-
不可以直接访问类的属性 / 对象方法
属性只有在创建对象的时候才会出现,保存在对象中;
而类是在第一次访问时,由类加载将类的代码保存在内存中的代码段里
可以在类方法中创建一个对象,进而在类方法执行时访问属性 / 对象方法 -
在对象方法中可直接调用类方法,注意不要用 self(因为类方法没有对象),直接写类名调用即可
类方法使用规范:
-
在创建一个类时,在类中创建一个同名的类方法,用来创建一个初始对象(apple 官方写的类都遵守该规范)
@interface Person : NSObject{ NSString* _name; int age; } + (Person*)person; + (Person*)personWithName:(NSString*)name AndAge:(int)num; @end @implementation Person + (Person*)person{ Person* p = [Person new]; p->_name = @"newBaby"; p->_age = 1; return p; } + (Person*)personWithName:(NSString*)name AndAge:(int)num{ Person* p = [Person new]; p->_name = name; p->_age = num; return p; } @end int main(){ Person* p1 = [Person new]; Person* p2 = [Person person]; Person* p3 = [Person personWithName:@"jack" AndAge:19]; }
03 NSString
NSString 是一个数据类型,用来保存 OC 字符串
其实是 Foundation 框架中的一个类 ---> OC 字符串本质上是用 NSString 对象来存储的
-
NSString 最常用的类方法
-
+ (instancetype)stringWithUTF8String:(const char *)nullTerminatedString;
---> 将 C 字符串转换为 OC 字符串对象
instancetype 作为返回值,表示将当前类的对象返回
char* str0 = "rose"; NSString * str1 = [NSString stringWithUTF8String:str0]; // str1 = @"rose";
-
+ (instancetype)stringWithFormat:(NSString *)format;
---> 使用变量或其他数据来拼接成 OC 字符串
int age = 10; NSString *name = @"jack"; [NSString stringWithFormat:@"大家好,我叫 %@,今年 %d 岁。",name, age];
-
-
NSString 最常用的对象方法
-
length 方法
---> 得到字符串的字符个数,可以处理中文。返回值为 NSUInteger 类型,其实就是 unsigned long
NSString *test1 = @"看这串数字123"; NSUInteger len = [str length]; NSLog(@"len = @lu", len); // len = 8
-
- (unichar)characterAtIndex:(NSUInteger)index;
---> 得到指定下标对应的字符. 返回值为 unichar 类型,其实等价于 unsigned short(2 字节 --> 可以处理中文)
NSString *test2 = @"看这串数字123"; unichar ch = [test2 characterAtIndex:2]; NSLog(@"ch = %C", ch); // ch = 串,注意 unichar 的占位符为 %C // 不可用小写字母(c表示一个字节 --> char)
-
- (BOOL)isEqualToString:(NSString *)aString;
---> 判断两个字符串是否相同。注意不要用 == ,不可靠
NSString *test3 = @"jack"; NSString *test4 = [NSString stringWithFromat:@"jack"]; // 注意,此时若用 == 判断,会认为二者不相等 if([test3 isEqualToString:test4]){ NSLog(@"Yes!"); }
-
- (NSComparisonResult)compare:(NSString *)string;
---> 比较字符串大小。返回值 NSComparisonResult 类型实际上是枚举,可以使用 int 类型接收结果
NSString * str1 = @"China"; NSString * str2 = @"Japan"; int res = [str1 compare:str2]; NSLog(@"res = %d", res); // res = -1 ---> 小于
-
04 匿名对象
---> 没有名字的对象。
当我们在创建一个对象时,如果没有使用指针来存储该对象的地址,即没有任何指针指向该对象,那么这个对象就是匿名对象。
-
如何使用?
[Person new]->_name = @"jack"; [[Person new] sayHi]; // 注意:上述两个语句操作的是两个不同的对象,每使用一次 new 就会创建一个新的对象
注意:
- 匿名对象只能一次,因为每次创建匿名对象都是不同的
- 使用情景:1)对象用完即抛,只使用一次其中的成员;2)方法的参数是对象,而这个对象调用者不会使用,仅用于将成员传递给方法,此时可传递一个匿名对象
05 属性的封装
-
面向对象的三大特征:
-
封装:
函数其实就是封装的体现,类是一种更高级的封装(将数据和行为封装为1个整体)
优点:屏蔽内部的实现,外界不需要知道内部的具体实现,只需要知道怎么用;方便操作;便于后期维护
-
继承
-
多态
-
-
实际编程时存在的问题:人的年龄应在 0 - 120 之间,而系统允许的范围过大
---> 不应该允许随意访问 Person 类的 _age 属性
---> 去掉 @public,不允许外界直接访问内部属性,避免错误赋值
那接下来如何对 _age 属性进行读写操作?---> 创建相应的对象方法(不可以用类方法,因为需要指定对象)
-
写:为属性赋值 ---> 创建 setter 方法
该方法有参数(目标赋值),无返回值。但实际的赋值结果需要经过逻辑检验,判断是否在允许范围内
方法的命名规范:方法名必须以 set 开头,后面接 去掉 _ 的属性名;参数名为去掉 _ 的属性名
- 读:取出(返回)属性的值 ---> getter 方法
无参数,有返回值,返回值类型与属性的类型相同。
方法的命名规范:方法名为去掉 _ 的属性名
@interface Person : NSObject { NSString *_name; int _age; } - (void)setAge:(int)age; - (void)setName:(NSString*)name; - (int)age; - (NSString *)name; - (void)showInfo; @end
注意:
- 必须去掉 @public
- 如果类的属性只在类的内部访问,则无须为其封装 getter & setter
- 使用规范:只要属性会被外部访问,即使不涉及逻辑验证,也要封装 getter & setter
-
06 对象之间的关系
6.1 组合关系
一个类是由其他类联合起来组合而成的,那么这个类和它们之间的关系为组合关系
// 在 Computer.h 文件中
// Computer 由 CPU Memory 组合而成
#import "CPU.h"
#import "Memory.h"
@interface Computer :NSObject
{
CPU *_cpu;
Memory *_memory;
}
@end
6.2 依赖关系
一个对象的方法的参数是另一个(类的)对象,则前者依赖于后者
// 在 Person.h 文件中
#import "Phone.h"
@interface Person : NSObject
{}
- (void) callWithPhone:(Phone *)phone; // 用 phone 打电话 ---> 依赖于 phone 类(才能实现功能)
@end
-
耦合度:描述当修改一个对象时,对另一个对象的影响程度
1)低耦合:对另一个对象影响较小甚至没有影响。
影响大 ---> 高耦合
2)高内聚:该对象仅仅做自己相关的事情 ---> 单一指责原则:一个类只做这个类的事情
6.3 关联关系
一个类作为另一个类的属性,但它们不是组合关系,而是【拥有】关系
例:电脑由CPU Memory组成 ---> 组合关系,电脑只由这两个类充当属性;
人养狗,人有电脑 ---> 关联关系,人不是由狗 / 电脑组成的
6.4 继承关系
* 会在后面详细展开来讲