美文网首页
OC基础易忘知识点

OC基础易忘知识点

作者: 洋洋丝 | 来源:发表于2017-06-20 01:38 被阅读10次
    屏幕快照 2017-06-22 上午12.15.06.png

    ];
    p->_size = 3.5;
    p->_color = 0;
    p->_model = 4;
    p->_cpu = 1;

    
    ####类
    
    创建对象的时候返回的地址其实就是对象的第0个属性的地址
         但是需要注意的是: 对象的第0个属性并不是我们编写的属性, 而是一个叫做isa的属性
         isa是一个指针, 占8个字节
         
         其实类也是一个对象, 也就意味着Person也是一个对象
         平时我们所说的创建对象其实就是通过一个 类对象 来创建一个 新的对象
         类对象是系统自动帮我们创建的, 里面保存了当前对象的所有方法
         而实例对象是程序自己手动通过new来创建的, 而实例对象中有一个isa指针就指向了创建它的那个类对象
    
    如下图:
    
    
    ![类对象](https://img.haomeiwen.com/i2264508/a9485608a39cffd8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    #####成员变量,局部变量,全局变量
    
        写在类声明的大括号中的变量, 我们称之为 成员变量(属性, 实例变量)
        成员变量只能通过对象来访问
        注意: 成员变量不能离开类, 离开类之后就不是成员变量 
        成员变量不能在定义的同时进行初始化
        存储: 堆(当前对象对应的堆的存储空间中)
        存储在堆中的数据, 不会被自动释放, 只能程序员手动释放
    
        写在函数和大括号外部的变量, 我们称之为全局变量
        作用域: 从定义的那一行开始, 一直到文件末尾
        局部变量可以先定义在初始化, 也可以定义的同时初始化
        存储: 静态区
        程序一启动就会分配存储空间, 直到程序结束才会释放
    
        写在函数或者代码块中的变量, 我们称之为局部变量
        作用域: 从定义的那一行开始, 一直到遇到大括号或者return
        局部变量可以先定义再初始化, 也可以定义的同时初始化
        存储 : 栈
        存储在栈中的数据有一个特点, 系统会自动给我们释放
    
    #####点语法
    
        如果给属性提供了getter和setter方法, 那么访问属性就又多了一种访问方式 , 点语法
        点语法其实它的本质是调用了我们的setter和getter方法
        点语法是一个编译器的特性, 会在程序翻译成二进制的时候将.语法自动转换为setter和getter方法
        如果点语法在=号的左边, 那么编译器会自动转换为setter方法
        如果点语法在=号的右边, 或者没有等号, 那么编译器就会自动转换为getter方法
         点语法的注意点:
         点语法一般用于给成员变量赋值, 如果不是给成员 
         变量赋值一般情况下不建议使用, 但是也可以使用     
         如调用test方法,可以写成p.test,相当于[p test]
    
    
    #####多态
    
         多态是事物的多种表现形态
    
         在程序中如何表现:
         父类指针指向子类对象
         
         优点:
         提高了代码的扩展性
         
         注意点:
         如果父类指针指向子类对象, 如果需要调用子类特有的方法, 必须先强制类型转换为子类才能调用
        
        动态类型: 在编译的时候编译器只会检查当前类型   对应的类中有没有需要调用的方法
        在运行时,系统会自动判断a1的真实类型
    
    

    例如:

    Animal:动物类

    import <Foundation/Foundation.h>

    @interface Animal : NSObject
    {
    int _age;
    }

    • (void)eat;
      @end

    import "Animal.h"

    @implementation Animal

    • (void)eat
      {
      NSLog(@"吃东西");
      }
      @end

    Dog狗类,继承动物类:

    import <Foundation/Foundation.h>

    import "Animal.h"

    @interface Dog : Animal

    • (void)kanJia;
      @end

    import "Dog.h"

    @implementation Dog

    • (void)eat
      {
      NSLog(@"啃骨头");
      }

    • (void)kanJia
      {
      NSLog(@"看家, 旺旺叫");
      }
      @end

    Animal *a1 = [Dog new];
    [a1 eat];

    编译时因为动物有eat方法,不报错。但是运行时判断为dog对象,调用dog对象的eat方法

    
    #####实例变量修饰符
    
    @public 就是实例变量修饰符
     
     @public
     >可以在其它类中访问被public修饰的成员变量
     >也可以在本类中访问被public修饰的成员变量
     >可以在子类中访问父类中被public修饰的成员变量
     
     @private
     >不可以在其它类中访问被private修饰的成员变量
     >可以在本类中访问被private修饰的成员变量
     >不可以在子类中访问父类中被private修饰的成员变量
     
     @protected
     >不可以在其它类中访问被protected修饰的成员变量
     >可以在本类中访问被protected修饰的成员变量
     >可以在子类中访问父类中被protected修饰的成员变量
     
     注意: 默认情况下所有的实例变量都是protected
     
     @package
     >介于public和private之间的
     如果是在其它包中访问那么就是private的
     如果是在当前代码所在的包种访问就是public的
    
    #####description
        NSlog(@"",moumoumou)
        %@是用来打印对象的, 其实%@的本质是用于打印字符串
        只要利用%@打印某个对象, 系统内部默认就会调用父类的description方法
        调用该方法, 该方法会返回一个字符串, 字符串的默认格式 <类的名称: 对象的地址>
        可以重写description方法, 返回我们需要打印的内容
        只要利用%@打印对象, 就会调用description
        如果通过%@打印对象就会调用-号开头的
        如果通过%@打印类对象就会调用+号开头的
    
        建议: 在description方法中尽量不要使用self来获取成员变量
        因为如果你经常在description方法中使用self, 可能已不小心就写成了 %@, self
        如果在description方法中利用%@输出self会造成死循环,例如下面:
    
    
    • (NSString *)description
      {
      return [NSString stringWithFormat:@"%@", self];
      }
    
    ####@porperty
    
    在Xocde4.4之前, 可以使用@porperty来代替getter/setter方法的声明
     也就是说我们只需要写上@porperty就不用写getter/setter方法的声明
    
     @synthesize是一个编译器指令, 它可以简化我们getter/setter方法的实现
     
     什么是实现:
     在声明后面写上大括号就代表着实现
      (1)在@synthesize后面告诉编译器, 需要实现哪个@property生成的声明
      (2)告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
        如果在@synthesize后面没有告诉系统将传入的值赋值给谁, 系统默认会赋值给和@synthesize后面写得名称相同的成员变量
    
    例如:
    @synthesize age;就代表实现age成员变量的get, set,不是_age.
    
    从Xcode4.4以后apple对@property进行了一个增强, 以后只要利用一个@property就可以同时生成setter/getter方法的声明和实现
     没有告诉@property要将传入的参数赋值给谁, 默认@property会将传入的属性赋值给_开头的成员变量
     
     @property有一个弊端: 它只会生成最简单的getter/setter方法的声明和实现, 并不会对传入的数据进行过滤
     如果想对传入的数据进行过滤, 那么我们就必须重写getter/setter方法
     如果不想对传入的数据进行过滤, 仅仅是提供一个方法给外界操作成员变量, 那么就可以使用@property
     
     如果利用@property来生成getter/setter方法, 那么我们可以不写成员变量, 系统会自动给我们生成一个_开头的成员变量
     注意: @property自动帮我们生成的成员变量是一个私有的成员变量, 也就是说是在.m文件中生成的, 而不是在.h文件中生成的
    
    如果重写了setter方法, 那么property就只会生成getter方法
    如果重写了getter方法, 那么property就只会生成setter方法
    如果同时重写了getter/setter方法, 那么property就不会自动帮我们生成私有的成员变量
    
    ####id类型
    
    id是一个数据类型, 并且是一个动态数据类型
    
    默认情况下所有的数据类型都是静态数据类型
         静态数据类型的特点: 
         在编译时就知道变量的类型, 
         知道变量中有哪些属性和方法
         在编译的时候就可以访问这些属性和方法, 
         并且如果是通过静态数据类型定义变量, 如果访问了不属于静态数据类型的属性和方法, 那么编译器就会报错
         
    动态数据类型的特点:
         在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型
         并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错
    
    #####类的本质
    
    类其实也是一个对象, 这个对象会在这个类第一次被使用的时候创建
         只要有了类对象, 将来就可以通过类对象来创建实例对象
         实例对象中有一个isa指针, 指向创建自己的类对象
         
     类对象中保存了当前对象所有的对象方法
         当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找
    
    
    ![Snip20150623_6.png](https://img.haomeiwen.com/i2264508/1a692817a227e781.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
    启动过程:
    
    只要程序启动就会将所有类的代码加载到内存中, 放到代码区。load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次。如果存在继承关系, 会先调用父类的load方法, 再调用子类的load方法。当当前类第一次被使用的时候就会调用(创建类对象的时候)。initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次。initialize用于对某一个类进行一次性的初始化。initialize和load一样, 如果存在继承关系, 会先调用父类的initialize再调用子类的initialize。
    
    
    #####内存管理
    
    野指针:
    只要一个对象被释放了, 我们就称这个对象为 "僵尸对象"。当一个指针指向一个僵尸对象, 我们就称这个指针为野指针。只要给一个野指针发送消息就会报错。为了避免给野指针发送消息会报错, 一般情况下, 当一个对象被释放后我们会将这个对象的指针设置为空指针。
    
    #####@class
    
    应用场景1:
    
    声明一个类
    
    应用场景2:
    
    防止循环拷贝,造成死循环。(import引入会拷贝文件内容,如果.h1引入.h2,.h2又引入.h1,会造成循环拷贝)
    
    #####Block
    
    MRC:
    
    返回值 (^block名字 )(参数列表)
    
    取别名
    
    typedef (^bieblock)(int,int)
    
    就可以用 bieblock来定义新block,类型为上面
    
    如:
    
    bieblock block1;
    block1 = ^(int value1, int value2){
            return value1 + value2;
        };
    

    // 1.block中可以访问外面的变量
    /*
    int a = 10;
    void (^myBlock)() = ^{
    NSLog(@"a = %i", a);
    };
    myBlock();

    打印a = 10

    // 2.block中可以定义和外界同名的变量, 并且如果在block中定义了和外界同名的变量, 在block中访问的是block中的变量
    /*
    int a = 10;
    void (^myBlock)() = ^{
    int a = 20;
    NSLog(@"a = %i", a);
    };
    myBlock();
    */

    打印a = 20;

    //

    // 3.默认情况下, 不可以在block中修改外界变量的值
    // 因为block中的变量和外界的变量并不是同一个变量
    // 如果block中访问到了外界的变量, block会将外界的变量拷贝一份到堆内存中
    // 因为block中使用的外界变量是copy的, 所以在调用之前修改外界变量的值, 不会影响到block中copy的值
    /*
    int a = 10;
    NSLog(@"&a = %p", &a);
    void (^myBlock)() = ^{
    // a = 50;
    NSLog(@"&a = %p", &a);
    NSLog(@"a = %i", a);
    };
    a = 20;
    myBlock();
    */

    /*
    // 如果想在block中修改外界变量的值, 必须在外界变量前面加上__block
    // 如果在block中修改了外界变量的值, 会影响到外界变量的值
    __block int a = 10;
    NSLog(@"&a = %p", &a);
    void (^myBlock)() = ^{
    a = 50;
    NSLog(@"&a = %p", &a);
    NSLog(@"a = %i", a);
    };
    myBlock();
    NSLog(@"a = %i", a);
    */

    /*
    

    // 默认情况下block存储在栈中, 如果对block进行一个copy操作, block会转移到堆中
    // 如果block在栈中, block中访问了外界的对象, 那么不会对对象进行retain操作
    // 但是如果block在堆中, block中访问了外界的对象, 那么会对外界的对象进行一次retain

    // 如果在block中访问了外界的对象, 一定要给对象加上__block, 只要加上了__block, 哪怕block在堆中, 也不会对外界的对象进行retain
    // 如果是在ARC开发中就需要在前面加上__weak
    

    内存管理:

    MRC:管理block
    总结:只要block没有引用外部局部变量,block放在全局区
    只要Block引用外部局部变量,block放在栈里面.
    block只能使用copy,不能使用retain,使用retain,block还是在栈里面
    只要block没有引用外部局部变量,block放在全局区

    ARC:管理block
    只要block引用外部局部变量,block放在堆里面

        block使用strong.最好不要使用copy
    
    
    #####copy属性
    
    // 如果是通过不可变对象调用了copy方法, 那么不会生成一个新的对象
    // 原因: 因为原来的对象是不能修改的, 拷贝出来的对象也是不能修改的
    // 既然两个都不能修改, 所以永远不能影响到另外一个对象, 那么已经符合需求
    // 所以: OC为了对内存进行优化, 就不会生成一个新的对象(比如nsstring->nsstring)
    
     正是因为调用copy方法有时候会生成一个新的对象, 有时候不会生成一个新的对象
         所以: 如果没有生成新的对象, 我们称之为浅拷贝, 本质就是指针拷贝
              如果生成了新的对象, 我们称之为深拷贝, 本质就是会创建一个新的对象
    
    // 3.注意点: copy block之后引发循环引用(MRC中)
    (
        // 如果对象中的block又用到了对象自己, 那么为了避免内存泄露, 应该将对象修饰为__block
        __block Person *p = [[Person alloc] init]; // 1
        p.name = @"lnj";
        NSLog(@"retainCount = %lu", [p retainCount]);
        p.pBlock = ^{
            NSLog(@"name = %@", p.name); // 2
        };
        NSLog(@"retainCount = %lu", [p retainCount]);
        p.pBlock();
        
       [p release]; // 1
    )
    
    
    ![屏幕快照 2017-06-22 上午12.10.13.png](https://img.haomeiwen.com/i2264508/a8b0c54130bc743c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
    
    ![Uploading 屏幕快照 2017-06-22 上午12.15.06_880264.png . . .]

    相关文章

      网友评论

          本文标题:OC基础易忘知识点

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