Day02-Object-C类与对象

1.对象在内存中的存储

1. 内存中的五大区域
    栈  存储局部变量
    堆 程序员手动申请的字节空间 malloc calloc realloc 函数
    BSS段 存储未被初始化的全局变量 静态变量
    数据段(常量区) 存储已被初始化的全局 静态变量 常量数据
    代码段 存储代码 存储程序的代码
2. 类加载
    1). 在创建对象的时候, 肯定是需要访问类的
    2). 声明1个类的指针变量也会访问类的
    在程序运行期间 当一个类第一次被访问到的时候, 会将这个类存储到内存中的代码段区域,这个过程叫做类加载
    只有类在第一次被访问的时候, 才会做类加载
    一旦类被加载到代码段以后, 直到程序结束的时候才会被释放
3. 对象在内存中存储
    Person *p1 = [Person new];
    1). Person *p1 会在栈内存中申请一块空间, 在栈内存中声明一个Person类型的指针变量p1. p1是个指针变量. 那么只能存储地址
    2). [Person new]; 真正在内存中创建对象的其实是这句
    new 做的事情
        a. 在堆内存中申请1块合适大小的空间
        b.在这个空间中根据类的模板创建对象
            类模板中定义了什么属性, 就把这些属性依次的声明在对象之中
            对象中还有另外1个属性, 叫做isa是个指针, 指向对象所属的类在代码段中的地址
        c.初始化对象的属性
            如果属性的类型是基本类型, 那么就赋值为0;
            如果是c语言的指针类型, 那么就赋值为null
            如果是OC的指针类型, 就赋值为nil
        d. 返回对象的地址
    3). 注意
        a. 对象中只有属性, 而没有方法, 自己类的属性外加1个isa指针指向代码段中的类
        b. 如何访问对象的属性
            指针名->属性名;
            根据指针, 找到指针指向的对象, 再找到对象中的属性来访问
        c. 如何调用方法
            [指针名 方法名]
        先根据指针名找到对象, 对象发现要调用方法, 再根据对象的isa指针找到类, 然后调用类里的方法
    4. 为什么不把方法存储在对象之中?
        因为每个对象的方法的代码实现都是一样的, 没有必要为每个对象都保存1个方法,太浪费空间了, 既然都一样, 那么就保存1份
    5. 对象的属性的默认值
        如果我们创建1个对象,没有为对象的属性赋值, 那么这个对象的属性是有值得    
        如果属性的类型是基本类型, 那么就赋值为0;
        如果是c语言的指针类型, 那么就赋值为null
        如果是OC的指针类型, 就赋值为nil 

2. nil与null

1. NULL
    只能作为指针变量的值, 如果1个指针变量的值是NULL值代表, 这个指针不指向内存中的任何1块空间
    NULL其实等价于0 NULL其实是1个宏,就是0
2. nil
    只能作为指针变量的值, 代表这个指针变量不指向内存中的任何空间. nil其实也等价于0 也是1个宏
3. 使用建议
    虽然使用NULL的地方也可以使用nil, 使用nil的地方也可以使用NULL, 但不建议随便使用
    C指针用NULL
        int *p1 =NULL;p1指针不指向内存中的任何1块空间
    oc的类指针用nil
        Person *p1 = nil; p1指针不指向任何对象
    4. 如果1个类指针的值为nil 代表这个指针不指向任何对象
    Person *p1 = nil;
    这个时候,如果通过p1指针去访问p1指针指向的对象的属性, 会运行报错
    如果通过p1指针调用对象的方法, 运行不报错, 但是方法不会被执行, 没有任何反应   

3.多个指针指向同一个对象

1.同类型的指针变量之间是可以相互赋值的
    Person *p1 = [Person new];
    Person *p2 = p1;这是完全没有问题的, p1和p2的类型都是Person 指针类型
    代表将p1的值赋值给p2 而p1的值是对象的地址, 所以就把对象的地址赋值给了p2
    所以p1和p2指向了同1个对象
    无论通过p1还是p2去修改对象, 修改的都是同一个对象

4.分组导航标记

1). #pragma mark 分组名
    就会在导航条对应的位置显示一个标题
2). #pragma mark -
    就会在导航条对应的位置显示1条水平分隔线
3). #pragma mark - 分组名
    就会在导航条对应的位置先产生1条水平分割线,再显示标题

5. 方法与函数

1. 在C中学习的叫函数,如:
    void test(){
    }
2. 在oc类中写的叫做方法,如:
    -(void) sayHi;
3. 相同点:
    都是用来封装1段代码的, 将1段代码封装在其中,表示1个相对独立的功能,函数或者方法只要被调用,那么封装在其中的代码就会被自动执行
4. 不同点
    1). 语法不同
    2)定义的位置不一样
        oc方法的声明只能写在@interface的大括弧的外面,实现只能写在@implementation之中. 函数除了在函数的内部和@interface的大括弧之中,其他的地方都是可以写的
        就算把函数写在类中, 这个函数仍然不属于类, 所以创建的对象中也没有这个函数
        注意;函数不要写到类中, 虽然这样是可以的, 但是不要这么做, 不符合规范
    3).调用的方式也不一样
        函数可以直接调用, 当时方法必须要先创建对象, 通过对象来调用
    4) 方法是属于类的,函数是孤立的

6. 易犯错误

1.@interface 是类的声明, @implementation是类的实现 他们之间是不能相互嵌套的
2. 类必须要先声明然后再实现
3. 类的声明和实现必须都要有 就算没有方法,类的实现也是必不可少的
4. 类的声明必须要放在使用类的前面, 实现可以放在使用类的后面
5. 声明类的时候,类的声明和实习必须要同时存在, 特殊情况下可以只有实现没有声明, 虽然可以这样, 但是不能这样写, 不规范
6. 属性名一定要以下划线开头, 规范, 类名 每个单词的首字母大写
7. 属性不允许声明的时候初始化, 在为类写1个属性的时候, 不允许在声明属性的时候为属性赋值
8. oc方法必须要创建对象通过对象名来调用
9. 方法只有声明没有实现时
    a. 编译器会给个警告, 不会报错
    b. 如果指针指向的对象, 有方法的声明 而没有实现, 那么通过这个指针来调用这个方法,在运行时会报错
        unrecognized selector sent to instance 0x00000
        只要看到这个错误, 说明要么对象中没有这个方法, 要么只有方法的声明而没有对应的实现.

7. 多文件开发

1. 所有的类都写在main.m这个源文件之中
    后期的维护就非常不方便, 不利于团队开发
2. 推荐方式
    把1个类写在1个模块之中, 而1个模块至少包含两个文件
    .h 头文件
        写的类声明, 因为要用Foundation框架中的类NSObject所以在这个头文件中要引入Foundation框架的头文件. 然后将类的声明的部分写在.h文件中
    .m实现文件
        先引入模块的头文件, 这样才会有类的声明, 再写上类的实现
    如果要用到类, 只需要引入这个模块的头文件就可以直接使用了.
3.添加类模块的更简洁的方式
    NewFile Cocoa Class 自动生成模块文件 .h .m
    自动的将类的声明和实现写好
    填写的名称是决定模块文件的名称, 类名是可以自己再改的
    建议模块的文件名和模块中的类名保持一致, 这样方便代码的管理
4. 当我们要使用这个类的时候, 需要先将这个类的头文件先引进来, 才可以使用

8. 对象和方法

1. 对象和方法
    对象作为方法的参数, 对象做为方法的返回值
2. 类的本质是我们自定义的1个数据类型, 因为对象在内存中的大小是由我们决定的, 多写几个属性, 对象就大一些, 少写几个, 对象占用的空间就小一些
    什么是数据类型: 是在内存中开辟空间的1个模板
3. 既然类是1个数据类型, 那么类就可以作为方法的参数
    -(void)test:(Dog *)dog;
    注意:
        1). 当对象作为方法的参数的时候, 参数应该怎么写, 参数类型是类指针
            -(void)test:(Dog *)dog;
        2). 调用方法的时候,如果方法的参数是1个对象
            那么给实参的时候,实参要求也必须是1个符合要求的对象,否则就会出问题
        3).当对象作为方法的参数传递的时候,是地址传递
            所以在方法内部通过形参去修改形参指向的对象的时候, 会影响实参变量指向的对象的值
    4. 对象也可以作为方法的返回值
        1.设么时候方法的返回值是1个对象呢?
            当方法执行完毕之后,如果有1个对象方法的内部不知道如何处理,并且这个对象是调用者翘首以盼的, 那么这个时候我们就应该将这个对象返回
        2.如果方法的返回值是1个对象, 那么返回值类型应该写  类指针    

9. 对象作为类的属性

1.对象作为类的属性
    1).类的属性代表什么?
        代表类所拥有的东西,设计类的时候, 我们怎么知道这个类有哪些属性呢?分析这类事物拥有什么东西.
        拥有的东西就可以作为这个类的属性
        人有姓名, 年龄, 性别, 身高, 
        空调类有:品牌, 颜色, 大小, 匹数
    2).类的方法代表什么?
        代表这类事物具有的行为, 具有的功能
        设计类的时候, 我们如何知道这个类有哪些方法呢
        那么就分析这类事物具有哪些行为,这个类事物具有哪些功能,会做什么?
        人:走路, 吃饭, 睡觉, 唱歌
        空调:制冷, 制热
2. 有1个人类, 还有1个狗类
    人养了一条狗,人拥有1条狗
    如何描述这个关系:人拥有1条狗
    人拥有1条狗, 那么就把狗作为人的1个属性
3. 对象完全可以作为另外1个类的属性, 代表这个类拥有这个对象
    A类拥有1个B对象, 那么就将B对象作为A类的属性
4. 当A类作为B类的属性的时候, 在创建B类对象的时候,B类对象中的属性仅仅只是1个指针变量,默认情况下并没有创建A对象
5. 重点:
    1).什么时候1个类可以作为另外1个类的属性
    2).属性的本质其实是1个变量
    3).如何访问对象的属性对象的成员

猜你喜欢

转载自blog.csdn.net/a90123/article/details/80563932
今日推荐