美文网首页iOSiOS Objective - C 基础iOS Dev
iOS学习之 Objective - C 基础知识点

iOS学习之 Objective - C 基础知识点

作者: 马文涛 | 来源:发表于2015-09-10 23:02 被阅读3331次

    Objective-C

    1. import的用法


    • 拷贝文件内容
      可以自动防止文件的内容被重复拷贝(#define宏定义)
    • Foundation 框架头文件的路径
      Xcode.app 显示包内容
      Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/Foundation.framework
    • 命令行指令
    • 编写 Oc 源文件: .m .c
    • 编译: cc -c xxx.m xxx.c
    • 链接: cc xxx.o xxx.o -framework Foundation(用到的时候才加)
    • 运行: ./a.out
    • 主头文件
      主头文件:最主要的头文件,名字一般跟框架名称一样,包含了框架中的所有其他头文件
    • Foundation框架的主头文件名称就是Foundation.h
      只需要包含Foundation框架主头文件,就可以使用整个框架的东西

    2. 对象方法和类方法


    • 对象方法: - 开头
      • 只能由对象调用
      • 对象方法中能访问当前对象的成员变量(实例变量)
    • 类方法: + 开头
      • 只能由类名来调用
      • 类方法中不能访问成员变量
      • 类方法的优点和使用场合:
      1. 不依赖于对象,执行效率高
      2. 能用类方法,尽量用类方法
      3. 场合:当方法内部不需要使用到成员变量时,就用类方法
      4. 可以允许类方法和对象方法同名

    3. 成员变量和局部变量


    • 成员变量:

      • 写在类声明的大括号中的变量, 我们称之为 成员变量(属性, 实例变量)成员变量只能通过对象来访问
      • 注意: 成员变量不能离开类,,离开类之后就不是成员变量,成员变量不能在定义的同时进行初始化
      • 存储: 堆(当前对象对应的堆的存储空间中)
      • 存储在堆中的数据, 不会被自动释放, 只能程序员手动释放
    • 局部变量:

    • 写在函数或者代码块中的变量, 我们称之为局部变量

      • 作用域: 从定义的那一行开始, 一直到遇到大括号或者return
      • 局部变量可以先定义再初始化, 也可以定义的同时初始化
      • 存储 : 栈
      • 存储在栈中的数据有一个特点, 系统会自动给我们释放
    • 全局变量

      • 写在函数和大括号外部的变量, 我们称之为全局变量
      • 作用域: 从定义的那一行开始, 一直到文件末尾
      • 局部变量可以先定义在初始化, 也可以定义的同时初始化
      • 存储: 静态区
      • 程序一启动就会分配存储空间, 直到程序结束才会释放

    4. 方法和函数


    • 方法
    • 函数属于整个文件,, 方法属于某一个类,方法如果离开类就不行
    • 函数可以直接调用, 方法必须用对象或者类来调用
    • 函数
    • 能写在文件中的任意位置(@interface和@end之间除外),函数归文件所有
    • 函数调用不依赖于对象
    • 函数内部不能直接通过成员变量名访问某个对象的成员变量

    5. setter 和 getter 用法简介


    • setter
    • 作用: 给成员变量赋值
    • 格式:
      1. 必须是对象方法
      2. 一定没有返回值
      3. 方法名称一定以set开头, set后面跟上成员变量的名称, 并去掉下划线, 然后将首字母大写
      4. 一定有参数, 并且参数类型和成员变量的类型一致, 参数名称就是成员变量的名称去掉下划线
    • getter
    • 作用: 返回成员变量的值
    • 格式:
      1. 必须是对象方法
      2. 一定有返回值, 返回值类型和成员变量的类型一致
      3. 方法名称就是成员变量的名称去掉下划线
      4. 一定没有参数
    • readLoad 和 readWrite
    • 一个属性可以只有getter方法, 没有setter方法, 这种属性我们称之为只读属性
    • 一个属性也可以只有setter方法, 没有getter方法, 这种属性我们称之为只写属性
    • 如果既有setter方法又有getter方法, 那么这种属性我们称之为可读可写的属性
    • 一个属性也可以没有getter和setter, 这种属性我们称之为私有属性

    6. self 的基本使用


    • 用法
    • 那个调用了当前方法,self就代表谁
    • self出现在对象方法中, self就代表对象
    • self出现在类方法中, self就代表类
    • 在对象方法利用 "self->成员变量名” 访问当前对象内部的成员变量(实例变量)
    • [self 方法名] 可以调用其他对象方法\类方法
    • self会自动区分类方法和对象方法, 如果在类方法中使用self调用对象方法, 那么会直接报错
    • 不能在对象方法或者类方法中利用self调用当前self所在的方法
    • 动态绑定:
      动态类型能使程序直到执行时才确定对象的真实类型
      动态类型绑定能使程序直到执行时才确定要对那个对象调用的方法
    • 使用self调用本方法,导致死循环调用
    • #pragma mark
    • 将代码分隔开,方便我们进行查找。

    7. super 基本使用


    • 编译器指令符号.
    • 利用super给父类的方法发送一个消息, 那么系统就会自动调用父类的方法
    • 如果以后想在子类中调用父类的方法可以使用super
    • 如果想在给父类方法进行扩展的同时保留父类的方法, 那么可以使用super调用父类同名的方法
    • super 在什么方法中就调用父类的什么方法

    8. 面向对象基本思想


    封装

    • 原理:屏蔽内部实现的细节,仅仅对外提供共有的方法/接口
    • 好处:保证数据的安全性
    • 规范:一般情况下不会对外直接暴露成员变量, 都会提供一些共有的方法进行赋值成员变量都需要封装起来

    继承

    • 父类必须声明在子类的前面
    • 不允许子类和父类拥有相同名称的成员变量, 因为子类继承父类,子类将会拥有父类的所有成员变量,若在子类中定义父类同名成员变量 属于重复定义。
    • 调用某个对象的方法时, 优先去当前类中找, 如果么有, 去父类中找
    • 基类的私有属性能被继承, 不能在子类中访问。
    • OC中的继承是单继承:也就是说一个类只能一个父类, 不能继承多个父类
    • 缺点:耦合性太强

    多态

    • 事物的多种形态
    • 没有继承就没有多态
    • 代码的体现: 父类类型的指针指向子类对象
    • 好处: 如果函数\方法参数中使用的是父类类型, 可以传入父类, 子类对象
    • 局限性: 父类类型的变量 不能 直接调用子类特有的方法,必须强转为子类类型变量后, 才能直接调用子类特有的方法
    • 动态绑定:
      • 动态类型能使程序直到执行时才确定对象的真实类型
      • 动态类型绑定能使程序直到执行时才确定要对那个对象调用的方法
      假设 子类 Dog 有一个特有的方法bark
      [dog bark];
      Animal *an = [Dog new];
      [(Dog*)an bark]; //把父类的指针,强制类型转换    
      
      

    9. 成员变量的作用域


    • @public 在任何地方都能直接访问对象的成员变量
    • @private 只能在当前类的对象方法中直接访问(子类可以通过seter geter方法访问父类的私有的成员变量)
    • @protected 能在当前类和子类的对象方法中直接访问 (默认是 protected)
    • @package 只要处在同一个框架中, 就能直接访问对象的成员变量(不常用)
      点语法使用注意
    • 点语法的本质还是方法调用
    • p.age = 10; // [p setAge:10]
    • 引发死循环
      self.age = age; // [self setAge:age]
    • 私有成员变量
    • 写在@implementation 中的成员变量,默认就是私有成员变量,并且和利用@private 修饰的不太一样,@implementation 中定义的成员变量在其他类中无法查看,也无法访问
      在@implementation 中定义的私有变量只能在本类中查看
    • 私有方法:只有实现没有声明,OC 中美有真正的私有方法,因为 OC 是消息机制。私有方法外面不能访问,只能通过包装成 sel 就可以访问

    10. @property 用法


    • Property 编译器指令

    • 生成setter 和 getter 方法声明(未加强版)

      -(void)setAge:(int)age;
      -(int)age;
       @property int age;
      
    • @synthesize age = _age;

    • setter和getter实现中会访问成员变量_age, 如果成员变量_age不存在,就会自动生成一个@private的成员变量_age

    • @synthesize age;

    • setter和getter实现中会访问@synthesize后同名成员变量age
      如果成员变量age不存在,就会自动生成一个@private的成员变量age

    • 多个属性可以通过一行@synthesize搞定,多个属性之间用逗号连接

      @synthesize age = _age, number = _number, name = _name;
      
      
    • Property 增强

    • 只要利用一个@property就可以同时生成setter/getter方法的声明和实现传入的属性赋值给_开头的成员变量

    • @property有一个弊端: 它只会生成最简单的getter/setter方法的声明和实现, 并不会对传入的数据进行过滤

    • 如果想对传入的数据进行过滤, 那么我们就必须重写getter/setter方法如果不想对传入的数据进行过滤, 仅仅是提供一个方法给外界操作成员变量, 那么就可以使用@property

    • 注意: 如果没有会自动生成一个_开头的成员变量,自动生成的成员变量是私有变量, 声明在.m中,在其它文件中无法查看,但当可以在本类中查看

    • 有就不生成,没有就生成

      • 如果重写了setter方法, 那么property就只会生成getter方法
      • 如果重写了getter方法, 那么property就只会生成setter方法
      • 如果同时重写了getter/setter方法, 那么property就不会自动帮我们生成私有的成员变量
    • @property(属性修饰符) 数据类型 变量名称;
      readwrite:代表生成 getter 和 setter 方法,默认就是
      readonly:代表只生成 getter 方法,(只读)

    • 修改 getter 方法名(常用)
      程序员之间有一个约定, 一般情况下获取BOOL类型的属性的值, 我们都会将获取的方法名称改为isXXX

    11. new alloc init 的基本用法及区别


    • alloc
    • 开辟存储空间
    • 将所有成员变量设为0
    • 返回当前的对象地址
    • init
    • 初始化成员变量, 但是默认情况下init的实现是什么都没有做 2.返回初始化后的实例对象地址
    • alloc和 init 返回的地址是一样的

    12. id和 instancetype


    • 静态类型和动态类型
    • 静态类型:将一个指针变量定义为特定类的对象时,使用的是静态类型,在编译的时候就知道这个指针变量所属的类,这个变量总是存储特定类的对象。
      Person *p = [Person alloc] init]]
    • 动态类型:这一特性是程序直到执行时才确定对象所属的类
      id p = [[Person alloc] init];
    • Id
    • id 是一种通用的对象类型,它可以指向属于任何类的对象,也可以理解为万能指针
    • id是动态类型,所以可以通过id类型直接调用指向对象中的方法, 编译器不会报错
    • 优点
      • 通过静态数据类型定义变量, 不能调用子类特有的方法
      • 通过动态数据类型定义变量, 可以调用子类特有的方法
      • 通过动态数据类型定义的变量, 可以调用私有方法
    • 弊端: 由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
    • 应用场景
    • 多态, 可以减少代码量, 避免调用子类特有的方法需要强制类型转换
    • 为了避免动态数据类型引发的运行时的错误, 一般情况下如果使用动态数据类型定义一个变量, 在调用这个变量的方法之前会进行一次判断, 判断当前变量是否能够调用这个方法
    id obj = [Student new];
    [obj isKindOfClass:[Student class]]
    //isKindOfClass , 判断指定的对象是否是某一个类, 或者是某一个类的子类
    
    • instancetype
    • instancetype == id == 万能指针 == 指向一个对象
    • id在编译的时候不能判断对象的真实类型
    • instancetype在编译的时候可以判断对象的真实类型
    • id和instancetype除了一个在编译时不知道真实类型, 一个在编译时知道真实类型以外, 还有一个区别
    • id可以用来定义变量, 可以作为返回值, 可以作为形参
    • instancetype只能用于作为返回值
    • 注意: 以后但凡自定义构造方法, 返回值尽量使用instancetype, 不要使用id

    13. 构造方法(- 开头的对象方法)


    • 用来初始化对象的方法.

    • 重写构造方法的注意:

    • 先调用父类的构造方法.

    • 再进行子类内部的成员变量的初始化

    • 返回当前对象的地址

       -(instancetype)init
       {
          // 注意: 不要把 = 号写为 ==
          // 一定要将[super init]的返回值赋值给self
          if (self = [super init]) {
          // 初始化子类
          _age = 6;
          }
              return self;
      }    
      
      
    • 自定义构造方法

    • 自己做自己的事情

    • 父类的属性交给父类的方法来处理,子类的方法处理子类自己独有的属性

    • 自定义构造方法必须以intiWith开头,并且’W’必须大写

    • 类工厂方法:

    • 用于快速创建对象的类方法, 我们称之为类工厂方法

    • 类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间

    • 规范:

      • 一定是类方法 +
      • 方法名称以类的名称开头, 首字母小写
      • 一定有返回值, 返回值是id/instancetype
      • 注意: 以后但凡自定义类工厂方法, 在类工厂方法中创建对象一定不要使用类名来创建,一定要使用self来创建
        return [[self alloc] init];

    14. Category - 分类


    • 在不改变原来类模型的前提下, 给类扩充一些方法. 有2种方式 : 继承 分类
    • 好处: 一个庞大的类可以分模块开发, 一个庞大的类可以由多个人来编写, 便于团队合作.
    • 使用注意:
    • 分类中写property, 只会生成getter/setter方法的声明, 不会生成实现和私有成员变量
    • Category 可以访问原始类的成员变量, 但不能添加变量, 只能添加方法. 如果想 添加变量, 可以考虑通过继承创 建子类
    • Category 可以实现原始类的方法, 不推荐这么做, 因为它是替换掉原始类的方 法, 这么做以后就不能访问原来的 方法.
    • 多个Category 中如果实现了相同的方法, 只有最后一个参与编译的才会有效.
    • 方法调用的优先级 : 分类(最后参与编译的分类优先) —>原来类—>父类
    • 类扩展(Extendsion)
    • 某个类扩充一些私有的成员变量和方法
    • 写在.m文件中
    • 英文名是Class Extension
    • 格式(俗称匿名分类)
          @interface 类名 ()
          @end
          
      

    15. 类的本质(typedef struct objc_class * Class)


    • 类也是一个对象, 是 class 类型的对象, 简称 “类对象”, 类名就代表着类对象, 每个类只有一个类对象
    • + load
    • + load : 在程序启动的时候会加载所有的类和分类, 并调用所有类和分类的 + load 方法,并且只会调用一次。
    • 加载顺序 父类 → 子类→ 分类。不管程序运行过程有没有用到这个类, 都会调用 + load 方法。
    • + initialize
    • + initialize : 在第一次使用某个类时(比如创建对象等), 且只会调用一次 + initialize 方法.
    • 主要用于对某一个类一次性初始化
    • 一个类只会调用一次 + initialize方法, 先调用父类的, 再调用子类的
    • 获取类对象的2种方式(获取内存中的类对象)
    ```
        Class c = [Person class]     // 类方法
        Person *p = [Person new];
        Class c1 = [p class];  // 对象方法
    
    ```
    
    • 类在内存的表现
      • 实例对象 → 类对象(对象方法)→ 元类对象(类方法)→ 根元类 (isa 指向自己)
    • 元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有则该元类会向它的父类查找该方法,直到一直找到继承链的头。
    • 元类(metaclass)也是一个对象,那么元类的isa指针又指向哪里呢?为了设计上的完整,所有的元类的isa指针都会指向一个根元类(root metaclass)。
    • 根元类(root metaclass)本身的isa指针指向自己,这样就行成了一个闭环。上面说到,一个对象能够接收的消息列表是保存在它所对应的类中的。在实际编程中,我们几乎不会遇到向元类发消息的情况,那它的isa 指针在实际上很少用到。不过这么设计保证了面向对象的干净,即所有事物都是对象,都有isa指针。
    • 由于类方法的定义是保存在元类(metaclass)中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。
    • 如下图 :


      Snip20150623_6.png

    16. NSLog


    • 使用 NSLog 和 %@输出某个类对象时, 会调用类对象 + description 方法, 并拿到返回值(NSString *)进行输出
    • - description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址)
      • description方法是基类NSObject 所带的方法. 使用NSLog输出OC对象,意义就不是很大,因为我们并不关心对象的内存地址,比较关心的是对象内部的一些成变量的值。因此,会经常重写description方法,覆盖description方法 的默认实现
        -(NSString *) description
        {
                return [NSString stringWithFormat:@"age = %d", _age]
        }
        
        
    • + descrption 方法
    • 当使用NSLog输出该类的类对象的时候调用*/(不常用
    • 注意: 死循环. 如果在 - description方法中使用 NSLog %@ 输出self对象会引发死循环

    17. SEL 基本使用


    • 代表方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系

    • 每个方法都有一个与之对应的 SEL类型的对象

      • SEL 其实是对方法的一种包装, 将方法包装成一个 SEL 类型的数据, 去找对应的方法地址, 进而进行调用
      • 注意:在这个操作过程中又缓存,第一次找的时候一个一个的找,非常耗性能,之后再用到的时候就直接使用
    • 对象是否实现了某个方法

      • - (BOOL) respondsToSelector: (SEL)selector 判断实例是否实现这样方法
      • + (BOOL)instancesRespondToSelector:(SEL)aSelector; (类对象)
    • 让对象执行某个方法

    • - (id)performSelector:(SEL)aSelector;

    • SEL 类型的定义 typedef struct objc_selector *SEL

      //SEL 对象的创建
       SEL s = @selector(test);
       SEL s2 = NSSelectorFromString(@"test”);
      // 将SEL对象转为NSString对象
      NSString *str = NSStringFromSelector(@selector(test));
      Person *p = [Person new];
      // 每个类都有以个_cmd 代表当前方法
      // 调用对象p的test方法
      [p  performSelector : @selector (test)];
      

    18. 内存管理


    Automatic Reference Couting

    • 什么是自动引用计数器
      • 每个OC对象都有自己的引用计数器,它是一个整数,从字面上, 可以理解为”对象被引用的次数”
      • 也可以理解为: 它表示有多少人正在用这个对象
      • 占4个字节

    Manul Refrence Counting

    • 什么是手动引用计数?

      • 所有对象的内容都需要我们手动管理, 需要程序员自己编写release/retain等代码
    • 方法的基本使用

      • retain : 计数器 +1 , 会返回对象本身
      • release : 计数器 -1 , 没有返回值(release并不代表销毁对象, 仅仅是计数器-1
      • retainCount : 获取当前的计数器
    • 概念

      • 僵尸对象: 所占用内存已经被回收的对象, 僵尸对象不能再使用
      • 野指针: 指向僵尸对象(不可用内存)的指针, 给野指针发送消息会报错(EXC_BAD_ACCES)
      • 空指针: 没有指向任何东西的指针(储存的东西是nil NULL 0), 给空指针发送消息不会报错
    • 内存管理代码规范
    • 只要调用了alloc, 必须有relese(autorelease), 如果对象不是通过alloc产生的, 就不需要release
    • set方法的代码规范
      • 基本数据类型: 直接复制
      -(void)setAge:(int)age
          {
          _age = age;
          }        
      ```
       -  OC对象类型
      ```
      -(void)setCar:(Car *)car
      {
      // 先判断是不是新传进来对象
          if( car != _car)
          {
      // 对旧对象做一次release
          [_car release]
      // 对新对象做一次retain
          _car = [car retain]
          }
      }
      ```
      
    • dealloc方法的代码规范
      • 对self(当前)所拥有的其他对象做一次release
      • 当一个对象要被回收的时候, 就会调用
      • 一定要调用[super dealloc], 这句调用放在最后面

    @Property 参数

    • set 方法内存管理相关的参数
      • retain : release 旧值 , retain 新值 (适用于OC对象类型)
      • assign : 直接赋值(默认, 适用于非OC对象类型)
      • copy : release 旧值, copy 新值
    • 是否要生成set方法
      • readwrite : 同时生成setter 和 getter的声明, 实现(默认)
      • readonly : 只会生成getter的声明, 实现
    • 多线程管理
      • nonatomic : 性能高 (一般就用这个)
      • atomic : 性能低(默认)
    • setter 和 getter方法的名称
      • setter : 决定了set方法的名称, 一定要有个冒号 :
      • getter : 决定了get方法的名称(一般用在BOOL类型)

    @Class(循环引用)

    • 仅仅告诉编译器,某个名称是一个类

    • 开发中引用一个类的规范

      • 在.h 文件中用@class 来声明类
      • 在.m 文件中用#import 来包含类的所有东西
    • 和#import 的区别(面试)

      • import会包含引用类的所有信息(内容),包括引用类的变量和方法
      • @class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知
    • 总结:

      • 如果都在.h中import, 假如A拷贝了B, B拷贝了C , 如果C被修改了, 那么B和A都需要重新拷贝. 因为C修改了那么B就会重新拷贝, 而B重新拷贝之后相当于B也被修改了, 那么A也需要重新拷贝. 也就是说如果都在.h中拷贝, 只要有间接关系都会重新拷贝
      • 如果在.h中用@class, 在.m中用import, 那么如果一个文件发生了变化, 只有和这个文件有直接关系的那个文件才会重新拷贝
      • 所以在.h中用@class可以提升编译效率
    • 两端循环引用(面试)

    • retain
      * 比如A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放。
      * 当两端互相引用时,应该一端用retain、一端用assign。

    • import
      * 如果两个类相互(#import<>)拷贝, 例如A拷贝B, B拷贝A, 这样会报错
      - 如何解决: 在.h中用@class, 在.m中用import
      - 因为如果.h中都用import, 那么A拷贝B, B又拷贝A, 会形成死循环
      - 如果在.h中用@class, 那么不会做任何拷贝操作, 而在.m中用import只会拷贝对应的文件, 并不会形成死循环

    @ autorelease基本用法

    • 会将对象放到一个自动释放池中,并且会返回对象本身
    • 当自动释放池被销毁时, 会对池子里面的所有对象做一次release 操作
    • 调用完@autorelease 方法后,对象计数器不变
    • @autorelease 的好处
      • 不用关心对象释放的时间
      • 不用关心什么时候调用 release
    • @autorelease 使用注意
      • 占用内存较大的对象不要随便用 autorelease
      • 占用内存较小的对象使用 autorelease,没有太大影响(影响:不能控制对象的释放时间)
    • 错误写法
      • alloc 之后调用了 autorelease ,又调用 release
        Person *p = [[[Person alloc] init] autorelease];
        [p release];
      • 连续调用autorelease(野指针错误,每个autorelease 释放时都会调用 release)
        Person *p = [[[[Person alloc] init] auturelease] autorelease]
    • 系统自带方法里面没有alloc、new、copy,说明返回的对象是autorelease的
      • 开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
      • 创建对象时不要直接用类名,一般用 self
      + (id)person
       {
           return [[[self alloc] init] autorelease];
       }
    
    • 自动释放池

    • 在 IOS 程序运行中,会创建无数个池子。这些池子都是以“栈”结构存在(先进后出,“杯子”)

    • 当一个对象调用 autorelease 方法时,会将这个对象放到栈顶的释放池 (”栈顶“ 相当于杯子底)

    • 集合对象的内存管理

      • 当把一个对象添加到集合中时,这个对象会做了一次retain操作,计数器会+1
      • 当一个集合被销毁时,会对集合里面的所有对象做一次release操作,计数器会-1
      • 当一个对象从集合中移除时,这个对象会一次release操作,计数器会-1

    ARC

    • ARC 的判断准则:主要没有强指针指向对象,就会释放对象

    • 指针的2种类型

    • 强指针:_strong 默认情况,所有指针都是强指针

    • 弱指针:_weak

      • _weak Person *p = [[Person alloc] init] 错误写法,没有意义的写法,一创建就释放
    • ARC 特点

      • 不允许调用 release、retain、retainCount
      • 允许重写 dealloc,但是不允许调用[super dealloc]
      • @property 的参数
      • strong : 成员变量是强指针(适用于 OC 对象类型)
      • weak : 成员变量是弱指针(适用于 OC 对象类型)
      • assign:适用于非 OC 对象
    • 循环引用
      一端用:strong,另一端用:weak

    • 在Compiler Flags一列加上-fno-objc-arc就表示禁止这个.m文件的ARC

    • mrc 可以转 arc ,系统转换

    19. copy


    • copy的基本原则
      • 因为拷贝要求修改原来的对象不能影响到拷贝出来得对象
      • 修改拷贝出来的对象也不能影响到原来的对象, 所以需要生成一个新的对象
      • 互不影响
    • copy的使用
      • 实现拷贝的方法有2个
        • copy:返回不可变副本
        • mutableCopy:返回可变副本
    • 普通对象实现拷贝的步骤
      • 遵守NSCopying协议
      • 实现-copyWithZone:方法
        • 创建新对象
        • 给新对象的属性赋值

    20. Block


    • block访问外面变量
      • block内部可以访问外面的变量
      • 默认情况下,block内部不能修改外面的局部变量
      • 给局部变量加上__block关键字,这个局部变量就可以在block内部修改
    • 利用typedef定义block类型
      typedef int (^MyBlock)(int, int);
      // 以后就可以利用MyBlock这种类型来定义block变量
    • block是存储在堆中还是栈中
      • 默认情况下block存储在栈中, 如果对block进行一个copy操作, block会转移到堆中
      • 如果block在栈中, block中访问了外界的对象, 那么不会对对象进行retain操作
      • 但是如果block在堆中, block中访问了外界的对象, 那么会对外界的对象进行一次retain
      • 如果在block中访问了外界的对象, 一定要给对象加上__block, 只要加上了__block, 哪怕block在堆中, 也不会对外界的对象进行retain
      • 如果是在ARC开发中就需要在前面加上__weak
      Person *p = [Person new];
      __weak Person *weakP = p;
      void (^myBlock) () = ^{
          
          weakP.age = 10;
      };
      myBlock();
      NSLog(@"age = %li", p.age);
      
      

    21. Protocol


    • 协议

    • @protocol 协议名称 < NSObeject >
      // 方法声明列表....
      @end

    • 如何遵守协议

    • 类遵守协议
      @interface 类名 : 父类名 <协议名称1, 协议名称2>
      @end

    • 协议遵守协议
      @protocol 协议名称 <其他协议名称1, 其他协议名称2>
      @end

    • 协议中方法声明的关键字

    • @required (默认): 要求实现,如果没有实现,会发出警告

    • @optional: 不要求实现,不会有警告

    • 定义一个变量的时候,限制这个变量保存的对象遵守某个协议

    • 如果没有遵守对应的协议,编译器会警告

      类名<协议名称> *变量名;
      id<协议名称> 变量名;
      NSObject<MyProtocol> *obj;
      id<MyProtocol> obj2;
      
    • @property中声明的属性也可用做一个遵守协议的限制

      @property (nonatomic, strong) 类名<协议名称> *属性名;
      @property (nonatomic, strong) id<协议名称> 属性名;
      @property (nonatomic, strong) Dog<MyProtocol> *dog;
      @property (nonatomic, strong) id<MyProtocol> dog2;
      
    • 协议可用定义在单独.h文件中,也可用定义在某个类中

    • 如果这个协议只用在某个类中,应该把协议定义在该类中

    • 如果这个协议用在很多类中,就应该定义在单独文件中

    • 分类可用定义在单独.h和.m文件中,也可用定义在原来类中

    • 一般情况下,都是定义在单独文件

    • 定义在原来类中的分类,只要求能看懂语法

    相关文章

      网友评论

      本文标题:iOS学习之 Objective - C 基础知识点

      本文链接:https://www.haomeiwen.com/subject/ldypcttx.html