undefined

  1. OC 程序的源文件的后缀名是 .m。 m 代表 message。 代表 OC 中最重要的 1 个机制 消息机制

  2. #import 是 #include 的增强版,将文件和内容在预编译的时候拷贝到写指令的地方。增强点:同一个文件无论 #Import 多少次,只会包含一次。#include 只能通过条件编译指令来实现

  3. Foundation 框架,提供了一些最基础的功能(输入输出、一些数据类型、NSLog 等)

  4. @autoreleasepool 自动释放池。

  5. NSLog 是 printf 的增强版,会输出一些调试相关信息、自动换行

    1
    2
    2022-07-09 10:43:47.115631+0800 basic_command_line[82785:1924379] Hello, World!
    [82785:1924379]: 进程pid 和 线程 tid
  6. NS 前缀。NextStep 公司来的,历史原因

  7. OC 中的绝大部分的关键字都是以 @ 符号开头

  8. 注释,单行注释和多行注释,和 C 语言一样

  9. 编译链接

    1
    2
    3
    4
    1. 将源代码编译为目标文件。预处理、检查语法、编译
    cc -c xx.m -o main.o
    2. 链接。注意程序中用到那个框架的功能,需要链接
    cc main.o -o main -framework Foundation
  10. OC 的数据类型,支持 C 语言所有数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    BOOL 类型,可以存储 YES、NO
    Boolean 类型,可以存储 true 或者 false
    BOOL 和 Boolean 在底层都是 unsigned char。YES 和 NO 底层就是数字 1 和 0
    class 类型,类
    id 类型,万能指针
    nil 和 NULL 差不多
    SEL 方法选择器
    block 代码段
  11. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    类的声明
    @interface 类名 : NSObject
    {
    @public
    NSString* _name; // 定义在大括号之中用来表示某类事物的共同特征的变量叫做属性(成员变量)
    }
    @end

    类的实现
    @implementation 类名
    @end

    类名首字母必须大写
    为类定义属性时,属性的名词必须要以下划线 _ 开头,这时一个规划。
    创建类的对象 class_name* p1 = [class_name new];
    默认情况下,对象的属性不允许访问。可以加一个 @public
    访问属性。对象名->属性 或 (*对象名).属性
    类方法:- (返回值)方法名:(参数类型)参数名 :(参数类型)参数名 :(参数类型)参数名;
    规范:如果方法只有1个参数,要求最好这个方法的名字叫做 xxxWithxx,提高可读性
  12. 创建对象,new 关键字。在堆内存中申请一块合适大小的空间,在这个空间中根据类的模板创建对象。类模板中定义了什么属性,就把这些属性依次的声明在对象之中。对象中还有另外一个属性,叫做 isa 指针,指向对象所属的类在代码段中的地址。

    1. 对象中只有属性,而没有方法。自己类的属性外加 isa 指针指向代码段中的类
    2. 如何调用方法,先根据指针名找到对象,对象发现要调用方法。再根据对象的 isa 指针找到类。然后再调用类中的方法。
  13. 对象属性的默认值。new 对象的时候,如果不赋值,就是赋默认值

    1. 如果属性的类型是基本数据类型,默认值是 0
    2. 如果属性的类型是 C 指针类型,默认值是 NULL
    3. 如果属性的类型是 OC 指针类型,默认值是 nil
  14. nil 和 NULL。NULL 就是一个宏,就是 0。nil 只能作为指针变量的值,也是一个 (void*)0

    C 指针用 NULL,OC 的类指针用 nil

    如果一个类指针的值为 nil,代表这个指针不指向任何对象。ClassName* p = nil。此时,用 p 指针访问对象属性,会运行报错;但是调用对象的方法,运行不会报错,但是方法不会执行,没有任何反应。

  15. 分组导航标记。

    1. #pragma mark 分组名 就会在导航条对应的位置显示一个标题。
    2. #pragma mark - 就会在导航条对应的位置显示一条水平分割线。
    3. #pragma - 分组名 在导航条对应位置先产生一条水平分割线,再显示标题
  16. 容易犯错的注意点

    1. @interface 和 @implement 他们之间不能相互嵌套
    2. 类必须要先声明再实现。类的声明和实现必要都要有
    3. 声明类的时候,类的声明和实现必须要同时存在。特殊情况下可以只有实现,没有声明,但是不规范
    4. 属性名一定要以下划线开头,这是规范。类名每个单词首字母大写
    5. 属性不允许声明的时候初始化
    6. 如果方法只有声明,没有实现。编译不会报错。new 一个对象去调用这个方法,会在运行时报错。
  17. command + B xcode 编译。

  18. 类指针作为方法的参数,一般是指针,实参和形参指向同一个对象,形参的改变可以影响实参。类指针作为方法的返回值,返回指针,指向对象。

  19. xcode 中,一个target 中类无法直接在另外一个 target 中访问

  20. 异常,try catch 语句。@finally 无论 try 中是否发生异常都会执行

    1
    2
    3
    4
    @try {
    } @catch(NSException* ex) {
    } @finally {
    }

    并不是所有的异常都能处理,比如 C 语言的异常无法处理,除数为 0 、空指针解引用。

  21. OC 方法,对象方法(声明使用 - 号)、类方法(声明使用 + 号)。

    1. 规范:一个类需要为这个类提供一个和类名同名的类方法,这个方法创建一个最纯洁的对象返回
  22. instanceType 作为返回值 代表返回的是当前这个类的对象 。

    1. 将 C 语言字符串转换成 NSString。
      + (**nullable** **instancetype**)stringWithUTF8String:(**const** **char** *)nullTerminatedCString;
  23. 对属性赋值,可以提供对象方法,同时将属性设置为私有。规范:setPropertyName 。取出属性的值,规范:getPropertyName d

  24. 对象之间的关系。组合关系、依赖关系、关联关系、继承关系。

    1. 组合关系,一个类由其他几个类联合起来组合而成,他们之间的关系就叫做组合关系
    2. 依赖关系,一个对象的方法的参数是另外一个对象,那么就说他们的关系是依赖关系
    3. 关联关系,关联体现的是两个类之间语义级别的一种强依赖关系。一个类作为另外一个类的属性,是拥有的关系
  25. Xcode 文档的安装

  26. OC 中的 static 不能修饰属性,也不能修饰方法。可以修饰方法中的局部变量。这个局部变量会放在静态区。

  27. self 关键字,self 是指针,在对象方法中指向当前对象,在类方法中指向当前类。在类方法中,self 指向当前这个类在代码段中的地址。

  28. 对象方法和类方法可以重名。

  29. 继承,子类拥有父类中所有成员。包括属性和方法,包括类方法和对象方法。

    1. 单根性,一个类只能有一个父类
    2. 传递性,A 类从 B 类继承,B 类从 C 类继承,那么A类则会继承 B 类和 C 类
    3. NSObject 是 Foundation 框架中的类,这个类有一个类方法 new。这个方法是用来创建对象的,方法的返回值是创建的这个对象的指针。还定义了属性 isa 指针,每一个继承此类的子类都有一个 isa 指针。
    4. OC 的类必须直接或间接的继承 NSObject 类,如果不继承,就无法创建对象。
    5. 子类中不能存在和父类同名的属性
  30. super 关键字,可以用在类方法和对象方法中,调用父类的对象方法或者类方法,不能访问属性

    1. 在对象方法中可以使用 super 关键字调用当前对象从父类继承过来的对象方法
    2. 在类方法中 super 关键字可以调用从当前类从父类继承过来的类方法
  31. 访问修饰符,用来修饰属性

    1. @private 私有,只能在本类的内部访问
    2. @protected 受保护,只能在本类以及子类中访问
    3. @package 可以在当前框架中访问
    4. @public 公共,都可以访问
    5. 如果不为属性指定访问修饰符,那么默认的就是 @protected
    6. 如果把类的属性写到类的实现中,使用大括号括起来。外界不仅不能访问,而且也看不到这个属性。是一个私有属性,各种修饰符无效。
  32. 私有方法,方法不写声明,只写实现,那么这个方法就是一个私有方法。只能在本类其他方法中调用,不能被外界调用。

  33. 里氏替换原则。子类可以替换父类的位置,并且程序的功能不受影响。一个父类指针指向一个子类对象

    1. NSObject 对象指针可以存储任意的 OC 对象的地址
    2. 当一个父类指针指向一个子类对象的时候,通过这个父类指针就只能调用子类对象中的父类成员。子类独有的成员无法访问
  34. 子类重写父类的方法,只需要在子类的实现中重新实现即可

  35. 使用 %@ 打印一个对象时,NSLog 函数的底层实现

    1. 调用当前对象的 description 方法
    2. 拿到这个方法的返回值 这个方法的返回值是一个字符串
    3. 将这个字符串输出
    4. description 方法是定义在 NSObject 类之中的,所以每个 OC 对象都有这个方法。这个方法返回:@<对象所属的类名:对象的地址>
    5. 可以重写 description 方法,打印出自己设定的当前类对象。
  36. 类在代码段中是如何存储的

    1. 先在代码段中创建 Class 对象,Class 是 Foundation 框架中的一个类。这个 Class 对象就是用来存储类信息的
    2. 将类的信息存储在这个 Class 对象之中。这个 Class 对象,至少有三个属性。类名:存储的这个类的名称;属性:存储的这个类具有哪些属性;方法:存储的这个类具有哪些方法
    3. 所以类以 Class 对象的形式存储在代码段的。存储类的这个 Class 对象,也叫做类对象,用来存储类的一个对象。所以存储类的类对象也有一个叫做 isa 指针的属性,这个指针指向存储父类的类对象。
    4. 调用类的类方法 class 就可以得到存储类的类对象的地址。Class c1 = [Person class];
  37. SEL 全称是 selector 选择器,是一个数据类型。其实是一个类,用来存储一个方法的。

    1. 如何将方法存储在类对象之中。先创建一个 SEL 对象,将方法的信息存储在这个 SEL 对象之中,在将这个 SEL 对象作为类对象的属性。
    2. 拿到存储方法的 SEL 对象。SEL 是 typdef 类型,已经是指针了。SEL s1 = @selector(sayHi);
  38. 调用方法的本质。[p sayHi] 内部的原理

    1. 先拿到存储 sayHi 方法的 SEL 对象,也就是拿到存储 sayHi 方法的 SEL 数据(SEL 消息)
    2. 将这个 SEL 消息发送给 p 对象
    3. 此时,p 对象接收到这个 SEL 消息以后,就知道要调用方法
    4. 根据对象的 isa 指针找到存储类的类对象
    5. 找到这个类对象以后,在这个类对象中去搜寻是否有和传入的 SEL 数据相匹配。如果有,就执行;如果没有,在找父类,直到 NSObject
    6. 调用方法的本质,其实为对象发送 SEL 消息
  39. 手动为对象发送 SEL 消息

    1. 先得到方法的 SEL 数据 SEL s1 = @selector(sayHi);
    2. 将这个 SEL 消息发送给 p 对象 [p performSelector:s1]; 其实就是调用 p 的方法

    注意:

    1. 如果方法有参数,方法名会有冒号 ,并且使用其他发送 SEL

      1
      2
      SEL s2 = @selector(sayHiWithName:);
      [p performSelector:s2 withObject:@"nihao"];
    2. 如果方法有多个参数,那就把这些参数封装到一个类中。

  40. 点语法。对象名.去掉下划线的属性名

    1. 点语法在编译器编译的时候,其实会将点语法转换为调用 setter、getter 的代码

      1
      2
      3
      4
      5
      6
      7
      // setter 方法
      - (void)setName:(NSString*)name;
      p.name = name; // 调用点语法就会转换成 setName 方法

      // getter 方法
      - (NSString*)name;
      NSString* str = p.name; // 调用点语法就会转换成 name 方法
    2. 在 getter 和 setter 方法中慎用点语法,可能造成无限递归

    3. setter 和 getter 方法的命名要符合规范,如果不符合,可能会出错

    4. 如果属性没有封装 setter 和 getter ,是无法使用点语法的

  41. property 作用:自动生成 getter 和 setter 方法的声明。应该写在 @interface 类的声明中

    1. 语法:@property 数据类型 : 名称 @property int age;
    2. property 的类型和属性的类型一致。@property 的名称和属性的名称一致(去掉下划线)
    3. @property 的名称决定了生成的 getter 和 setter 方法的名称。@property 的数据类型决定了生成的 setter 方法的参数类型和 getter 方法的返回值类型
    4. @property 只是生成 getter 和 setter 方法的声明,实现还要自己来,属性还要自己定义
  42. @synthesize 作用:自动生成 getter、setter 方法的实现,所以应该写在类的实现之中

    1. 语法 @synthesize @property的名称;
    2. 生成一个真私有的属性,属性的类型和 @synthesize 对应的 @property 类型一致。属性的名字和 @synthsize 对应的 @property 名字一致
    3. 自动生成 setter 方法的实现,实现的方式:将参数直接赋值给自动生成的那个私有属性。并且没有做任何的逻辑验证
    4. 自动生成 getter 方法的实现,实现的方式:将生成的私有属性的值返回
  43. 希望 @synthesize 不要自动生成私有属性,getter 和 setter 的实现中操作我们已经写好的属性就好了

    1. @syntheszie @property 名称 = 已经存在的属性名;
    2. 不会再去生成私有属性;直接生成 setter、getter 的实现。
      1. setter 的实现:把参数的值直接赋值给指定的属性
      2. getter 的实现:直接返回指定的属性的值
    3. 如果 setter 或 getter 有自己的逻辑验证,那就自己在类中重写即可
    4. 批量声明,如果多个 @property 的类型一致,可以批量声明 @property float height, weight;
    5. @synthesize 也可以批量声明,类型不一致也可以 @synthesize name=_name, age=_age, weight=_weight;
  44. 从 xcode 4.4 以后,xcode 对 @property 做了增强,只需要写一个 @property ,编译器就会自动生成私有属性(带下划线)、自动生成 getter、setter 的声明和实现。也可以批量声明

    1. 如果 setter 和 getter 同时重写,就不会自动生成私有属性,需要自己定义
    2. 如果 setter 和 getter 只有一个重写,另外一个编译器会自动生成
    3. 可以被继承,但是属性是私有的,不能直接访问,只能通过 setter 和 getter 方法访问
  45. 动态类型和静态类型

    1. 静态类型:指的是一个指针指向的对象是一个本类对象
    2. 动态类型:指的是一个指针指向的对象不是一个本类对象
    3. 编译检查,判断原则:看指针所属的类型之中有没有这个方法,如果有则通过,如果没有则报错。
  46. NSObject 是 OC 中所有类的基类,因此 NSObject 是一个万能指针,可以指向任意的 OC 对象。缺点:如果要调用指向的子类对象的独有的方法,就必须要做类型转换。

  47. id 指针,是一个万能指针,可以指向任意的 OC 对象。类似于 void*

    1. NSObject 和 id 的异同,都是万能指针,指向任意对象
    2. NSObject 指针去调用对象的方法时,编译器会做编译检查;通过 id 类型的指针去调用对象的方法时,编译器不会做编译检查。
    3. id 指针只能调用方法,不能使用点语法。
  48. instancetype 代表方法的返回值是当前类的对象。instancetype 是一个有类型的、代表当前类的对象。id 是一个无类型的指针,是一个没有类型的指针。

  49. 判断指针指向的对象中是否有这个方法可以执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 判断指针指向的对象中是否有这个方法可以执行。
    - (BOOL)respondsToSelector:(SEL)aSelector;
    if ([p respondsToSelector:@selector(sayHi)]) {}

    // 判断类中是否有指定的类方法
    + (BOOL)instancesRespondToSelector:(SEL)aSelector;

    // 判断对象是否是 指定类的对象或者子类对象
    - (BOOL)isKindOfClass:(Class)aClass;
    if ( [p isKindOfClass:[Person class]]) {}

    // 判断对象是否为指定类的对象,不包括子类
    - (BOOL)isMemberOfClass:(Class)aClass;
    if ( [p isMemberOfClass:[Person class]] ) {}

    // 判断类是否为另外一个类的子类
    + (BOOL)isSubclassOfClass:(Class)aClass;
  50. new 方法的内部,其实是先调用 alloc 方法,在调用 init 方法。

    1. alloc 方法是一个类方法,作用:哪一个类调用这个方法,就创建这个类的对象,并把对象返回
    2. init 方法 是一个对象方法,作用:初始化对象
  51. init 方法,为对象的属性赋初始值。也叫做构造方法。

    1. 重写 init 方法规范:
      1. 必须先调用父类的 init 方法,然后将方法的返回值赋值给 self 。
      2. 调用 init 方法初始化对象有可能会失败,如果失败,返回就是 nil。判断父类是否初始化成功,判断 slef 的值是否为 nil,如果不为 nil 说明初始化成功
      3. 如果初始化成功,就初始化当前对象的属性。最后返回 self 的值
    1
    2
    3
    4
    5
    6
    7
    8
    - (instancetype)init {
    self = [super init];
    if (self != nil) {
    // initialize self
    self.name = @"jack";
    }
    return self;
    }
    1. 为什么要调用父类的 init 方法?因为父类的 init 方法会初始化父类的属性,所以必须要保证当前对象中的父类属性也同时被初始化。
    2. 为什么要赋值给 self ?因为调用父类的 init 方法 会返回初始化成功的对象。实际上返回的就是当前对象,但是我们要判断是否为 nil
  52. 自定义构造方法。规范

    1. 自定义构造方法的返回值必须要是 instancetype
    2. 自定义构造方法的名称必须以 initWith 开头
    3. 方法的实现和 init 的要求一样
  53. 引用计数器。用来记录当前对象的正在使用的次数

    1. 每一个对象都有一个 retainCount 的属性,叫做引用计数器,类型是 unsigned long,占据 8 个字节。
    2. 当这个对象的引用计数器变为 0 的时候,代表这个对象无人使用,运行时会自动回收这个对象。
  54. 如何操作引用计数器

    1. 为对象发送一条 retain 消息,对象的引用计数器就会加一
    2. 为对象发送一条 release 消息,对象的引用计数器就会减一
    3. 为对象发送一条 retainCount 消息,就可以拿到对象的引用计数器的值
  55. 内存管理的分类

    1. MRC:Manual Reference Counting 手动引用计数,手动内存管理。当多一个人使用对象的时候,要求程序员手动的发送 retain 消息。少一个人使用的时候程序员手动的发送 release 消息
    2. ARC:Automatic Reference Counting 自动引用计数,自动内存管理。系统自动的在合适的地方发送 retain release 消息
  56. IOS 5 开始,Xcode 4.2 开始就支持 ARC。Xcode 默认 ARC 开发。关闭 ARC 开启 MRC。Objective-C Automatic Reference Counting 设置为 NO

    1. 重写 dealloc 方法的规范,必须要调用父类的 dealloc 方法,并且一定要放在最后。
    1
    2
    3
    4
    5
    6
    7
    Person* p = [[Person alloc]init];
    // 获取引用计数器的当前引用次数
    NSInteger count1 = [p retainCount];
    // 引用计数器加一
    [p retain];
    // 引用计数器减一
    [p release];
    1. 在 ARC 机制下,retain、release、dealloc 这些方法无法调用。
    2. MRC 机制下,当 release 后对象的引用计数器变为0,会被立即回收。
  57. 僵尸对象,一个已经释放的对象,但是这个对象所占的空间还没有分配给别人,这样的对象叫做僵尸对象。通过野指针去访问僵尸对象时,如果这块空间还没有分配给别人,那就没有问题。如果分配给别人了,那就会出现问题。

    1. 我们希望只要对象是僵尸对象,无论如何都不允许访问。xcode 提供僵尸对象的实时检查机制。target -> edit scheme -> Run -> Diagnostics -> Zombie object
    2. xcode 默认不打开僵尸对象检测。打开之后,再每打开一个对象时,都会先检查是否为僵尸对象,比较消耗性能
    3. 如何避免僵尸对象错误,当一个指针成为野指针以后,将这个指针的值设置为 nil 。当一个指针指为 nil,通过这个指针去调用对象的方法(包括使用点语法)的时候,不会报错,只是没有任何反应。但是如果直接访问属性,就会报错。
  58. 内存泄露,单个对象的内存泄露的情况

    1. 有对象的创建,而没有对应的 release
    2. retain 的次数和 release 的次数不匹配
    3. 在不适当的时候,为指针赋值为 nil
    4. 在方法中为传入的对象进行不适当的 retain
  59. 在 MRC 机制下, 当属性是一个 OC 对象时,这个属性的 setter 方法的写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - (void)setCar(Car*)car {
    if (_car != car) {
    [_car, release];
    _car = [car retain];
    }
    }
    // 重写 dealloc 方法
    - (void)dealloc {
    [_car release];
    [super delloc];
    }
  60. @property 参数。这几个参数没有顺序

    1. 与多线程相关的两个参数:atomic、nonatomic
      1. atomic:生成的 setter 方法会加锁,线程安全,效率低
    2. 与生成的 setter 方法的实现相关的参数:assign、retain
      1. assign:默认值。生成的 setter 方法就是直接赋值
      2. retain:生成的 setter 方法就是标准的 MRC 内存管理代码。也就是先判断新旧对象是否为同一个对象,如果不是 release 旧的,retain 新的。当属性的类型是 OC 对象类型时使用 retain。非 OC 对象使用 assign。
      3. retain 参数只是生成标准的 setter 方法(MRC内存管理代码),但是不会生成dealloc 中 release 的代码,所以我们还要自己手动在 dealloc 中 release
    3. 与生成只读、读写相关的参数:readonly、readwrite
      1. readwrite:默认值,会同时生成 setter、getter 方法
      2. readonly:只会生成 getter,不会生成 setter
    4. 与生成的 getter setter 方法名字相关的参数:getter、setter
      1. 默认情况下,@property 生成的 getter、setter 方法的名字都是标准的名字
      2. 其实可以通过参数来指定 @property 生成的 getter 方法的方法名字
      3. 注意:指定 setter 方法的名字时,因为 setter 方法带参数,因此方法名需要加上冒号(:)
      4. 如果使用 getter、setter 修改了生成方法的名字,在使用时,编译器会自动转换
  61. 当两个类相互包含的时候,会出现循环引用的问题。解决方案:其中一边不要使用 #import 引入对方的头文件,而是使用 @class 类名; 来标注这是一个类。这样子就可以在不引入对方头文件的情况下,告诉编译器是一个类。可以在 .m 文件中再 #import 对方的头文件,就可以使用。

    1. @class 和 #import 的区别
      1. #import 是将指定文件的内容拷贝到写指令的地方
      2. @class 并不会拷贝任何内容。只是告诉编译器这是一个类
  62. 循环 retain 。当两个对象互相引用的时候,A 对象的属性是 B 对象,B 对象的属性是 A 对象。

    1. 解决方案:一端使用 retain,另外一端使用 assign。使用 assign 的那一端在 dealloc 中不再需要 release
  63. 自动释放池。存入到自动释放池的对象,在自动释放池被销毁的时候,会自动调用存储在该自动释放池中的所有对象的 release 方法

    1
    2
    3
    // 创建自动释放池 
    @autoreleasepool {
    }
    1. 如何将对象存储到自动释放池中?在自动释放池之中调用对象的 autorelease 方法,就会将这个对象存入到当前自动释放池之中。这个 autorelease 方法返回的是对象本身,所以可以这么写

      1
      2
      3
      @autoreleasepool {
      Person* p1 = [[[Person alloc] init] autorelease];
      }

      自动释放什么时候结束。@autoreleasepool 大括号范围结束时。

    2. 注意点

      1. 只有在自动释放池中调用了对象的 autorelease 方法,这个对象才会被存储到这个自动释放池
      2. 对象的创建可以在自动释放池的外面
      3. 当自动释放池结束时,仅仅是对存储在自动释放池的对象发送一次 release 消息,而不是销毁对象
      4. 如果在自动释放池中,调用同一个对象的 autorelease 方法多次,就会将对象存储多次到自动释放池中。在自动释放池结束时,会发送多条 release 消息
      5. autorelease 方法和 对象的 release 和 retain 不冲突,都会起作用
      6. 自动释放池可以嵌套
  64. 类方法的规范,apple 的框架中的类也遵守这个规范

    1. 一般情况下,要求提供与自定义构造方法相同功能的类方法,这样可以快速创建一个对象
    2. 使用类方法创建的对象,要求这个对象在方法中就已经被 autorelease 过了。
  65. ARC(Automatic Reference Counting)自动引用计数。系统自动的帮助我们去计算对象的引用计数器的值。

    1. 当 ARC 开启时,编译器会自动的在合适的地方插入 retain、release、autorelease 代码。

    2. ARC 机制下,只要没有强指针指向这个对象,这个对象就会立即回收。本质:对象的引用计数为 0 的时候,自动释放。

    3. 强指针:默认情况,声明一个指针就是强指针。也可以使用 __strong 来显式声明这是一个强指针。

      1
      2
      Person* p;
      __strong Person* p;
    4. 弱指针:使用 __weak 标识的指针就是弱指针。

      1
      __weak Person* p;
    5. 强弱指针唯一的区别就是 ARC 模式下,他们用来作为回收对象的基准。

    6. 两种情况叫做没有任何强指针指向对象

      1. 指向对象的所有强指针被回收掉
      2. 指向对象的所有强指针赋值为 nil
    7. 在 ARC 机制下,当对象被回收的时候,原来指向这个对象的弱指针会自动置为 nil

  66. ARC 机制下,@property 参数不能使用 retain,因为 retain 会生成属性标准的 getter 代码,ARC 机制不需要这些逻辑。

  67. @property 生成的属性是强指针还是弱指针(strong、waek)。

  68. 在 ARC 机制下,当两个对象互相引用的时候,如果两边都使用 strong,那么就会出现内存泄露。建议一端使用 strong、一端使用 weak

  69. ARC 和 MRC 机制如何兼容?在 targets -> Build Phases -> MRC的类 -> 右击 ,填写 -fno-objc-arc 即可。

  70. xcode 可以将整个 MRC 程序转换为 ARC 程序 Edit -> Convert -> To Objective-C ARC 。建议不要转,xcode 无法从 ARC 转成 MRC

  71. category 。如果一个类模块有太多的方法,就显得很臃肿、维护成本增加。category 可以将一个类分成多个模块。

    1. 添加 oc 文件,选择 category 文件类型,以及对应类。会生成一个 .h 和一个 .m 的模块。

      1. 模块的文件名:本类名+分类名.h 本类名+分类名.m
    2. 添加的 category 也分为声明和实现

      1
      2
      3
      4
      @interface 本类名 (分类名)
      @end
      代表不是新创建一个类,而是对已有的类添加一个分类,小括号中写上这个分类的名字
      因为一个类可以添加多个分类,为了区分每一个分类,所以分类要取名字
    3. category 的使用。如果要访问分类中定义的成员,需要把分类的头文件引进来

    4. category 的注意:

      1. category 只能增加方法,不能增加属性
      2. 在 category 中可以写 @property ,但是不会自动生成私有属性。也不会自动生成 getter、setter 的实现,只会生成 getter、setter 的声明
      3. 在 category 的方法实现中不可以直接访问本类的真私有属性(定义在本类的 implementation 之中),但是可以调用本类的 getter、setter 方法来间接访问属性
      4. 当分类中有和本类中同名的方法时,优先调用分类的方法,哪怕没有引入分类的头文件。如果多个分类中有相同的方法,优先调用最后编译的分类。
  72. 非正式协议:为系统自带的类写分类。相当于为已经存在的类添加方法。对全局都会生效

  73. ARC 机制和其他语言的垃圾回收机制的区别

    1. ARC 不是运行时,是在编译的时候就在合适的地方插入 retain、release 等,当引入计数器为 0 时,立即销毁。
    2. ARC 是编译时,其他语言的拉取回收机制是运行时