【Objective-C】3 封装

第三节 封装


01 异常处理

  • 错误
    一般情况下,是指源代码不符合语法规范,出现编译报错,使程序无法编译
  • bug
    程序可以编译 链接 执行,但结果与预期不符 ---> 通过调试寻找产生 bug 的原因
  • 异常
    程序可以链接 编译 执行,但在处于某种特定状态下,程序会终止运行(崩溃),不会执行后面的代码

处理异常的目的:为了让程序执行发生异常时不崩溃,而是继续向下执行

语法:

@try
{
// 将有可能发生异常的代码放入 @try{} 中
// 当这段代码执行时,若发生异常,之前从当前位置跳转到 @catch,执行其 {} 中的代码
// 若未发生异常,则略过 @catch 部分  
}
@catch(NSException *ex){
// 一般 @catch 中的代码用来处理 / 记录异常
  // 可以通过 %@ 打印 catch 的参数 ex 指向的对象的值,即发生异常的原因
}
// @try{ } @catch{ } 执行结束后,会继续执行后面的代码

注意:

  1. 可以在 @catch{ } 后面接一个 @finally{ } ,其中的代码无论是否发生异常都会被执行

  2. 该方法不是万能的,不是所有运行时的异常都会处理(比如,无法处理 C 语言的异常)

  3. 实际开发过程中,该方法使用较少 ---> 避免异常最常用的方法还是逻辑判断(if 语句)

    // 不使用上述方法如何处理 / 避免异常?
    // 例:通过以下方式,先判断类中是否存在 sayHi 方法,再调用
    Person* p1 = [Person new];
    if([p1 respondsToSelector:@selector(sayHi)]){
      [p1 sayHi];
    }
    

02 类方法

  • OC 中的方法有两种:

    1. 对象方法 / 实例方法:如果想调用方法必须先创建对象,再使用对象名来调用
    2. 类方法:不依赖于对象,直接通过类名来调用
    // 声明:
    @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]; // [类名 类方法]
    

类方法特点:

  1. 不需要创建对象,节约空间;直接找到类 - 类中的方法,提高效率

  2. 不可以直接访问类的属性 / 对象方法

    属性只有在创建对象的时候才会出现,保存在对象中;
    而类是在第一次访问时,由类加载将类的代码保存在内存中的代码段里
    可以在类方法中创建一个对象,进而在类方法执行时访问属性 / 对象方法

  3. 在对象方法中可直接调用类方法,注意不要用 self(因为类方法没有对象),直接写类名调用即可

类方法使用规范:

  1. 在创建一个类时,在类中创建一个同名的类方法,用来创建一个初始对象(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 最常用的类方法

    1. + (instancetype)stringWithUTF8String:(const char *)nullTerminatedString;

      ---> 将 C 字符串转换为 OC 字符串对象

      instancetype 作为返回值,表示将当前类的对象返回

      char* str0 = "rose";
      NSString * str1 = [NSString stringWithUTF8String:str0];
      // str1 = @"rose";
      
    2. + (instancetype)stringWithFormat:(NSString *)format;

      ---> 使用变量或其他数据来拼接成 OC 字符串

      int age = 10;
      NSString *name = @"jack";
      [NSString stringWithFormat:@"大家好,我叫 %@,今年 %d 岁。",name, age];
      
  • NSString 最常用的对象方法

    1. length 方法

      ---> 得到字符串的字符个数,可以处理中文。返回值为 NSUInteger 类型,其实就是 unsigned long

      NSString *test1 = @"看这串数字123";
      NSUInteger len = [str length];
      NSLog(@"len = @lu", len); // len = 8
      
    2. - (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)
      
    3. - (BOOL)isEqualToString:(NSString *)aString;

      ---> 判断两个字符串是否相同。注意不要用 == ,不可靠

      NSString *test3 = @"jack";
      NSString *test4 = [NSString stringWithFromat:@"jack"];
      // 注意,此时若用 == 判断,会认为二者不相等
      
      if([test3 isEqualToString:test4]){
        NSLog(@"Yes!");
      }
      
    4. - (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. 使用情景:1)对象用完即抛,只使用一次其中的成员;2)方法的参数是对象,而这个对象调用者不会使用,仅用于将成员传递给方法,此时可传递一个匿名对象

05 属性的封装

  • 面向对象的三大特征:

    1. 封装:

      函数其实就是封装的体现,类是一种更高级的封装(将数据和行为封装为1个整体)

      优点:屏蔽内部的实现,外界不需要知道内部的具体实现,只需要知道怎么用;方便操作;便于后期维护

    2. 继承

    3. 多态

  • 实际编程时存在的问题:人的年龄应在 0 - 120 之间,而系统允许的范围过大

    ---> 不应该允许随意访问 Person 类的 _age 属性

    ---> 去掉 @public,不允许外界直接访问内部属性,避免错误赋值

    那接下来如何对 _age 属性进行读写操作?---> 创建相应的对象方法(不可以用类方法,因为需要指定对象)

    1. 写:为属性赋值 ---> 创建 setter 方法

      该方法有参数(目标赋值),无返回值。但实际的赋值结果需要经过逻辑检验,判断是否在允许范围内

    方法的命名规范:方法名必须以 set 开头,后面接 去掉 _ 的属性名;参数名为去掉 _ 的属性名

    1. 读:取出(返回)属性的值 ---> getter 方法

    无参数,有返回值,返回值类型与属性的类型相同。

    方法的命名规范:方法名为去掉 _ 的属性名

    @interface Person : NSObject
    {
        NSString *_name;
        int _age;
    }
    - (void)setAge:(int)age;
    - (void)setName:(NSString*)name;
    - (int)age;
    - (NSString *)name;
    - (void)showInfo;
    @end
    

    注意:

    1. 必须去掉 @public
    2. 如果类的属性只在类的内部访问,则无须为其封装 getter & setter
    3. 使用规范:只要属性会被外部访问,即使不涉及逻辑验证,也要封装 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 继承关系

* 会在后面详细展开来讲

posted @ 2022-03-02 09:33  Z/z  阅读(59)  评论(0)    收藏  举报