iOS 开发实训第三周周报

学习笔记

( 摘自《iOS开发:从零基础到精通》)

  • id 和 instancetype

    • id 类型:通用对象类型,类似于 C 语言中的 void *,可以指向任意一个继承了 NSObject 类的对象(id 本身就是一个指针,所以使用时不需要加星号),是 Objective-C 动态绑定的基础
    // 示例:一个数组中存储了 NSNumber 和 NSString 两种类型的对象,因为不知道每个对象的类型,所以可以用 // id 这种通用对象类型
    int main(int argc, const char *argv[]) {
        @autoreleasepool {
            NSString *array = @[@123, @"456"];
            id obj = array[1];
            NSLog(@"%@", obj);
        }
        return 0;
    }
    
    • instancetype 类型:在类方法中,例如,以 alloc、new 开头的方法,以及实例方法中 autorelease、init、retain 等方法的返回值类型就是 instancetype 类型,这些就称为关联返回类型的方法,即返回值是一个以方法所在的类为类型的对象,使用 instancetype 作为方法返回值的好处是可以确定对象类型,以便帮助编译器更好地定位代码问题
    • id 和 instancetype 的异同:
      • 相同点:都可以作为方法的返回值类型
      • 不同点:
        • id 可以作为方法或函数的参数的类型,也可以单独使用这种类型定义变量,但是 instancetype 不行
        • 如果方法的返回值类型是 instancetype,那么它返回的一定是这个类型的对象;如果方法的返回值类型是 id,那么它返回的是未知类型的对象
  • 懒加载:

    • 当需要获取某个属性的值时,再对该属性对象的实例变量进行初始化,从而提升内存的使用效率
    • 懒加载实际上就是对属性的 getter 方法进行重写,可以对属性进行一些初始化的操作
    // 使用懒加载初始化 name 属性,在初始化时给其赋值
    -(NSString *)name {
        if (_name == nil) { // _name 是 name 属性对应的实例变量
            _name = [NSString stringWithFormat:@"hello"];
        }
        return _name;
    }
    
  • 公有属性和私有属性:

    • 公共属性:在 .h 文件中声明的属性,可供外部调用
    • 专有属性:在 .m 文件中声明,只能在该类内部使用
  • 属性关键字:

    • 原子性:

      • atomic(默认):
        • 意味着多线程中只有一个线程能访问实例变量
        • 是线程安全的,但是会影响访问速度
        • 在非 ARC(自动引用计数)编译环境下,需要设置访问锁来保证对该变量进行正确的 getter/setter
      • nonatomic:
        • 多个线程可以同时对其进行访问
        • 非线程安全的,访问速度快
        • 当两个不同的线程对其访问时,容易失控
    • 存取方法:

      • readwrite(默认)

      • readonly

      • 在声明属性时,可以指定存取方法的自定义名称,通常用于 BOOL 类型属性的 getter 方法,例如

      @property(nonatomic, getter=isHidden) BOOL hidden;
      
    • 内存管理:

      • strong(默认):强引用,表示实例变量对传入的对象要有所有权关系,引用计数加1
      • weak:弱引用,在 setter 方法中,对传入的对象不进行引用计数加1的操作,当该对象引用计数为0时,该对象被释放,用 weak 声明的实例变量指向 nil,即实例变量的值为0
      • assign:简单复制,不改变索引计数,适用于简单数据类型,如 int、float、double、NSInteger、CGFloat等
      • copy:用于在内存中保留一份传入值的复制,而不是值自身的情况,即把原来的对象完整地复制到另外一个新的内存区,当副本改变时,原对象并不同时改变,同样当原对象发送改变时,其副本也不会发生改变,因为原对象和复制对象存储在两个独立的内存区域中,copy 和 strong 的区别在于实例变量是对传入对象的副本拥有所有权,而非对象本身
  • 方法名称:

    • 一个方法的实际名称是所有签名关键词的串联,包括冒号,例如
    -(void)insertString:(NSString *)aString atIndex:(NSUInteger)loc;
    

    方法名是 insertString:atIndex:

  • 消息处理机制:

    • 在 Objective-C 中,消息是直到运行时才和方法进行绑定关联的,关键在于编译器为类和对象生成的结构,其中类的结构中包含两个基本元素:一个指向父类的指针和一个类的方法列表;而当对象被创建时,对象的第一个实例变量是一个指向该对象的“类结构”的指针(isa 指针),通过该指针,就可以访问到该类及其父类的方法列表
    • 当向某个对象发送消息时:
      • 首先根据 isa 指针,找到该对象对应的类结构的方法列表,继而即可找到具体的方法实现;当在本类的方法列表中找不到对应的方法时,会根据类结构中父类的指针去查找父类的方法列表,直至 NSObject 根类
      • 将对象以及参数传递给找到的方法实现
      • 执行方法中的代码,获取方法的返回值
  • 对象操作:

    • 判断对象的类型:isKindOfClass
    • 判断对象是否响应消息:respondsToSelector
    • 对象间的比较:isEqual
    • 对象复制:copy
  • const 关键字:

    • const 修饰离其最近的变量
    • const 和 宏 #define 的对比:
      • 涉及常量的定义都建议用 const(并且都放到一个类中),其处理性能比宏定义高,因为当多次使用该常量时,只要在内存中创建一个对象即可,而如果使用宏定义,在程序编译时会把所有的宏都替换成常量,当程序运行时,会创建多个对象,占用多个内存区域,执行效率低一点
  • 数组的复制:

    • 直接赋值:浅复制,在内存中只有一个对象
    • arrayWithArray:深复制
  • int、NSInteger 和 NSNumber 的对比:

    • int 类型的大小是由系统的架构决定的
    • NSInteger 是一种动态定义的类型,会根据系统使用最大的 int
    • NSInteger 是基础类型,NSNumber 是一个类,如果需要在数组中存储一个数值,不能直接使用 NSInteger ,因为 Objective-C 的集合中存储的数据必须是对象
  • Block:

    • Block 块是封装工作单元的对象,是可以在任何时间执行的代码段,其本质是可移植的匿名函数

    • 可以作为方法和函数的参数传入,也可以从方法和函数中返回

    • 可以把 Block 赋给一个变量,这个变量就是指向 Block 的指针,调用已定义的 Block 时类似于函数调用

      // 定义
      int (^printBlock)(int) = ^(int inputNum) {
          NSLog(@"printBlock Called!");
          return inputNum;
      }
      
      // 调用
      int i = printBlock(100);
      
    • 可以把 Block 声明为类的属性(需要使用 copy 关键字),可以使用点语法来对属性进行取值和赋值

    • 操作 Block 外部的变量:

      • 访问 block 外的变量
      int main(int argc, const char *argv[]) {
          @autoreleasepool {
              int i = 100;
              void (^beginBlock)(void) = ^(void) {
                  NSLog(@"i: %d", i);	 // 如果 i 定义在 Block 的定义之前则可以访问
                  // i = 200; // 报错,此时 Block 是不能修改 Block 外定义的变量的
              };
              beginBlock(); // 输出100
              i = 200;
              beginBlock(); // 输出100,说明 Block 只有在定义时能捕获一次 i,后面 i 改变 Block 是无法捕获的
              NSLog(@"i: %d", i); // 输出200
          }
          return 0;
      }
      
      • 修改 block 外的变量:要在变量声明时加上 __block 关键字
      int main(int argc, const char *argv[]) {
          @autoreleasepool {
              __block int i = 100;
              void (^beginBlock)(void) = ^(void) {
                  NSLog(@"i: %d", i);	 // 如果 i 定义在 Block 的定义之前则可以访问
                  // i = 200; // 此时 Block 可以修改 Block 外定义的变量
              };
              beginBlock(); // 输出100
              i = 200;
              beginBlock(); // 输出200,说明此时 Block 能捕获到 i 的变化
              NSLog(@"i: %d", i); // 输出200
          }
          return 0;
      }
      
    • Block 回调

      • 定义带 Block 参数的方法
      • 设置 Block 的回调时机
      • 定义 Block 中需要执行的操作
  • 分类 Category:

    • 当需要对一个类新增一些新方法时使用,特别是针对系统自定义的类,如 UIView、 UIImageView 等
    • 注意:
      • 不要用分类去重写已存在的方法
      • 通过使用分类添加的方法,不仅针对该类有效,对其子类也有效,例如:给 UIView 添加了分类,UIImageView、UIButton 等子类也能用
      • 类 + 分类名称必须唯一
  • KVC:

    • Key-value coding 键值编码

      // 赋值
      -(void)setValue:(nullable id)value forKey:(NSString *)key; // 简易路径赋值
      -(void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; // 复合路径赋值
      
      // 取值
      -(nullable id)valueForKey:(NSString *)key; // 简易路径取值
      -(nullable id)valueForKeyPath:(NSString *)keyPath; // 复合路径取值
      
    • 修改 readonly 的属性以及私有属性:KVC 和点语法都可以用于对象属性的赋值,但是点语法只能给未被标记为 readonly 的属性赋值,而 KVC 是更加底层的技术,可以给 readonly 的属性赋值;此外,通过 runtime 遍历出来的私有属性/私有变量也能通过 KVC 进行赋值和取值(不推荐)

  • KVO:

    • Key Value Observing 观察者模式,当数据模型的属性值改变后,作为监视器的视图组件就会触发特定的方法,在该方法中可以获取数据模型变更的数值,从而更新界面 UI
  • 对象复制:

    • 深复制和浅复制

    • 可变对象复制和不可变对象复制:

      对象对象类型copymutableCopy
      NSString非容器类不可变对象浅复制深复制
      NSMutableString非容器类可变对象深复制深复制
      NSArray容器类不可变对象浅复制深复制
      NSMutableArray容器类可变对象深复制深复制
      NSDictionary容器类不可变对象浅复制深复制
      NSMutableDictionary容器类不可变对象深复制深复制

      (注意:容器类对象内的对象都是浅复制)

    • 自定义对象复制:实现 copyWithZone 方法(遵守 NSCopying 协议)

  • 宏定义:

    • 无参宏

    • 有参宏:

      • 注意参数要加上括号,因为宏体中传入的参数可以是一个表达式

        // 期望的是 ((3+4)*(3+4)),如果不加括号,则会变成 (3+4*3+4)
        # define SQUARE(a) ((a)*(a))
        
        int a = 3, b = 4;
        NSLog(@"square = %d\n", SQUARE(a+b)) 
        
      • 运算符 “#” 把跟在其后的参数转换成一个字符串

        # define STRING(n) #n
        
        NSLog(@"%s", STRING(test)) // 输出 test
        
      • 运算符 “##” 用于把多个参数连接到一起(很少用)

        # define CONNECT(a,b,c) (a##b##c)
        
        NSLog(@"%f", CONNECT(13.6, 2, 3)) // 输出 13.623000
        
  • 预处理指令 #include、#import、@class 对比:

    • #include:在指令处展开被包含的文件,标准 C 编译器至少支持八重嵌套包含,重复引用会报错
    • #import:大部分功能和 #include 一样,但是解决了重复引用的问题
    • @class:引入一个类时效率更高,但是不能使用引入类中的属性和方法
  • 常用的占位符:

    • %@:对象
    • %d %i:整数
    • %u:无符号整形
    • %f:浮点/双字
    • %x %X:二进制整数
    • %o:八进制整数
    • %zu:size_t
    • %p:指针
    • %e:浮点/双字(科学计算)
    • %g:浮点/双字
    • %s:C 字符串
    • %.*s:Pascal 字符串
    • %c:字符
    • %C:unichar
    • %lld:64位长整数
    • %llu:无符号64位长整数
    • %Lf:64位双字
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值