[Objective-C]第五天

 复习:

 1. Xcode文档的安装与使用.

 2. static关键字修饰方法中的局部变量.

 3. self关键字.

    1). 在对象方法中.

        父类的成员对于子类来讲,也是属于子类的.所以父类的成员在子类的方法中也可以使用self

    2). 在类方法中.

 4. 继承.

    1). 继承的目的:

    2). 继承的语法:

    3). 继承的效果:

    4). 继承是类在继承而不是对象在继承.

    5). 满足继承的关系.

        is a

    6). 继承的特点.

    7). NSObject类.

    8). 访问修饰符.

 5. 私有属性和私有方法.

 6. 里氏替换原则.

    1).什么是LSP

        Person *p1 = [Student new];

    2).为什么LSP是可以的.

    3).LSP的表现形式.  父类指针指向子类对象.

    4).LSP的好处.

    5).当1个父类指针指向1个子类对象的时候.只能通过这个父类指针访问这个对象中的父类成员.

 7. 方法重写

    1). 什么时候需要方法重写?

    2). 如何重写

    3). LSP的时候 通过指针调用的方法被子类重写了 那么调用的是子类重写的.

 8. 多态.

02-继承的本质

 1. 创建1个对象,这个对象在内存中是如何分配的.

  1). 子类对象中有自己的属性和所有父类的属性.

  2). 代码段中的每1个类都有1个叫做isa的指针,这个指针指向它的父类.

      一直指到NSObject

          [p1 sayHi];  //假设p1是Person对象.

         先根据p1指针找到p1指向的对象,然后根据对象的isa指针找到Person类.

         搜索Person类中是否有这个sayHi方法 如果有执行

         如果没有 就根据类的isa指针找父类 。。。。。

         NSObject 中如果没有就报错.

 03-作业

 1.人类

 姓名 性别 年龄.

 2.书类

 书名 作者 出版社 出版日期.

 3.学生类

 姓名 性别 年龄 学号 书.

 读书

 日期: 年 月 日.

 ------------------------------------------------

 1. 结构体与类的相同点

 都可以将多个数据封装为1个整体.

struct Date
{
    int year;
    int month;
    int day;
};

     
 @interface Date : NSObject
 {
     int year;
     int month;
     int day;
 }
 @end

 2. 结构体与类的不同点

  1). 结构体只能封装数据,而类不仅可以封装数据还可以封装行为.

  2). 结构体变量分配在栈空间 (如果是1个局部变量的情况下)

    而对象分配在堆空间.

  栈的特点: 空间相对较小. 但是存储在栈中的数据访问的效率更高一些.

  堆的特点: 空间相对较大. 但是存储在堆中的数据访问的效率相对要低

  存储在栈中的数据访问效率高 存储在堆中的数据访问效率低.

 3). 赋值.

  结构体 Student

  类:   Person

  Student s1 = {"jack",19,GenderMale};

  Student s2 = s1;

  Person *p1 = [Person new];

  Person *p2 = p1;

 3. 应用场景.

  1). 如果表示的这个实体 不仅是由多个数据组成, 这个是实体还有行为,只能使用类.

  2). 如果表示的实体没有行为.光有属性.

    a. 如果属性较少.只有几个. 那么这个时候就定义为结构体 分配在栈 提高效率.

    b. 如果属性较多.不要定义成结构体. 因为这样结构体变量会在栈中占据很大1块空间

    反而会影响效率.

    定义为类.

 04-类的本质

 1. 内存中的五大区域.

     栈

     堆

     BSS段

     数据段

     代码段.

    代码段:是用来存储代码的.

    类加载. 当类第1次被访问的时候 这个类就会被加载到代码段存储起来.

 2. 讨论三个问题

    1). 类什么时候加载到代码段.

        类第1次被访问的时候,类就会被加载到代码段存储 类加载.

    2). 类以什么样的形式存储在代码段.

    3). 类一旦被加载到代码段之后 什么时候回收.

        是不会被回收的 除非程序结束.

 3. 类是以什么样的形式存储在代码段的.

    1). 任何存储在内存中的数据都有1个数据类型.

         int num = 12;

         float  12.12f

         'a'

      任何在内存中申请的空间也有自己的类型.

        Person *p1 = [Person new];

    2). 在代码段存储类的那块空间是个什么类型的.

        在代码段中存储类的步骤

        a. 先在代码段中创建1个Class对象, Class是Foundation框架中的1个类.

            这个Class对象就是用来存储类信息的.

        b. 将类的信息存储在这个Class对象之中.

            这个Class对象.至少有3个属性

             类名: 存储的这个类的名称.

             属性s: 存储的这个类具有哪些属性

             方法s: 存储的这个类具有哪些方法.

        所以.类是以Class对象的形式存储在代码段的.

        存储类的这个Class对象 我们也叫做类对象. 用来存储类的1个对象.

        所以,存储类的类对象也有1个叫做isa指针的属性 这个指针指向存储父类的类对象.

 4. 如何拿到存储在代码段中的类对象.

    1). 调用类的类方法 class 就可以得到存储类的类对象的地址.

    2). 调用对象的对象方法 class 就可以得到存储这个对象所属的类的Class对象的地址.

    3). 对象中的isa指针的值其实就是代码段中存储类的类对象的地址.

    注意:

    声明Class指针的时候 不需要加* 因为在typedef的时候已经加了*了.

 5. 如何使用类对象.

    1). 拿到存储类的类对象以后.

        Class c1 = [Person class];

        c1对象就是Person类.

        c1 完全等价于 Person

    2). 使用类对象来调用类的类方法.

        因为类对象就代表存储在这个类对象中类.

        Class c1 = [Person class];

        c1就代表Person类.

        所以在使用Person的地方完全可以使用c1代替.

        比如我们使用类名来调用类方法.

        [Person sayHi];

        完全可以使用c1来调用. 因为c1就是Person

        [c1 sayHi];

    3). 可以使用类对象来调用new方法 创建存储在类对象中的类的对象.

        Person *p1 = [Person new];

        Class c1 = [Person class];

        其实创建Person对象 也可以这么做.

        Person *p2 = [c1 new];

    4).注意:

        使用类对象 只能调用类的类方法,因为类对象就等价于存在其中的类.

        Class c1 = [Person class];

        c1就是Person。

 -------------------------------

    1.  类是以Class对象的形式存储在代码段之中的.

    2.  如何拿到存储类的类对象.

    3.  有神马用?

        可以使用类对象调用类的类方法.

        Class c1 = [Person class];

        要调用Person的类方法 可以使用Person去调用.

        也可以使用c1去调用.

 05-SEL

 1. SEL 全称叫做  selector 选择器.

    SEL 是1个数据类型. 所以要在内存中申请空间存储数据.

    SEL其实是1个类. SEL对象是用来存储1个方法的.

 2. 类是以Class对象的形式存储在代码段之中.

    类名:存储的这个类的类名. NSString

    还要将方法存储在类对象之中.如何将方法存储在类对象之中.

     1). 先创建1个SEL对象.

     2). 将方法的信息存储在这个SEL对象之中.

     3). 再将这个SEL对象作为类对象的属性.

 3. 拿到存储 方法的SEL对象.

     1). 因为SEL是1个typedef类型的 在自定义的时候已经加*了.

        所以 我们在声明SEL指针的时候 不需要加*

     2). 取到存储方法的SEL对象,

        SEL s1 = @selector(方法名);

 4. 调用方法的本质.

     [p1 sayHi];

     内部的原理:

     1). 先拿到存储sayHi方法的SEL对象,也就是拿到存储sayHi方法的SEL数据. SEL消息.

     2). 将这个SEL消息发送给p1对象.

     3). 这个时候,p1对象接收到这个SEL消息以后 就知道要调用方法

     4). 根据对象的isa指针找到存储类的类对象.

     5). 找到这个类对象以后 在这个类对象中去搜寻是否有和传入的SEL数据相匹配的.

        如果有 就执行  如果没有再找父类 直到NSObject

        OC最重要的1个机制:消息机制.

        调用方法的本质其实就是为对象发送SEL消息.

        [p1 sayHi]; 为p1对象发送1条sayHi消息.

 5. 重点掌握:

    1).方法是以SEL对象的形式存储起来.

    2).如何拿到存储方法的SEL对象.

 6. 手动的为对象发送SEL消息.

    1). 先得到方法的SEL数据.

    2). 将这个SEL消息发送给p1对象.

    调用对象的方法 将SEL数据发送给对象.

        - (id)performSelector:(SEL)aSelector;

Person *p1 = [Person new];
SEL s1 = @selector(sayHi);
[p1 performSelector:s1]; //与 [p1 sayHi]效果是完全一样的.

    3). 调用1个对象的方法有两种.

        1). [对象名 方法名];

        2). 手动的为对象发送SEL消息.

 7. 注意事项:

      1). 如果方法有参数 那么方法名是带了冒号的.

      2). 如果方法有参数,如何传递参数.

        那么就调用另外1个方法.

        - (id)performSelector:(SEL)aSelector withObject:(id)object;

        - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

      3). 如果有多个参数

           把多个参数封装在一个对象里面。。

  ---------------------------------

总结:

  1. 类是以Class对象的形式存储在代码段.

  2. 如何取到存储类的类对象.

  3. 如何使用类对象调用类的类方法

  4. 方法是以SEL数据的形式存储的.

  5. 调用方法的两种方式.

 06-点语法

 1. Java、 C# 对象可以使用点语法来访问对象的成员.

     OC中也有点语法. OC中也可以使用点语法来访问对象的属性.

     但是OC的点语法和Java C# 是完全不一样的.

     OC的对象如果要为属性赋值或者取值 就要调用对应的getter或者setter.

 2. 使用点语法来访问对象的属性.

     语法:

     对象名.去掉下划线的属性名;

     p1.name = @"jack"; 这个时候就会将@"jack"赋值给p1对象的_name属性.

     NSString *name = p1.name; 把p1对象的_name属性的值取出来.

 3.点语法的原理.

    p1.age = 18;

    这句话的本质并不是把18直接赋值给p1对象的_age属性.

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

    1). 当使用点语法赋值的时候. 这个时候编译器会将点语法转换为调用setter方法的代码.

        对象名.去掉下划线的属性名 = 数据;

         转换为:

        [对象名 set去掉下划线的属性名首字母大写:数据];

        p1.age = 10;

        [p1 setAge:10];

     2).当使用点语法取值的时候.这个时候编译器会将点语法转换为调用getter方法的代码.

         对象名.去掉下划线的属性名;

         转换为:

         [对象名 去掉下划线的属性名];

         int age = p1.age;

         int age = [p1 age];

 4.注意.

     1). 在getter和setter中慎用点语法,因为有可能会造成无限递归 而程序崩溃,

     2). 点语法在编译器编译的时候 会转换为调用setter getter方法的代码.

         p1.name = @"jack";

         [p1 setName:@"jack"]

         NSString *name = p1.name;

         NSString *name = [p1 name];

        如果我们的setter方法和getter方法名不符合规范 那么点语法就会出问题.

     3). 如果属性没有封装getter setter 是无法使用点语法的

        因为点语法的本质是getter setter方法.

 07-@property

 1. 我们写1个类.

     a. 要先为类写属性.

     b. 在声明属性的getter setter

     c. 再实现getter setter

      哎! 1点点技术含量都没有.有没有更为简单的方式来实现同样的代码效果呢

 2. @property

     1). 作用: 自动生成getter、setter方法的声明.

        因为是生成方法的声明,所以应该写在@interface类的声明之中.(大括号之后)

     2). 语法:

         @property 数据类型 名称;

@interface Pro : NSObject{
    NSString *_name;
    int _age;
    float _height;
    
}
@property NSString *name;
@property int age;
@property float height;
@end

     3). 原理:

        编译器在编译的时候.会根据@property生成getter和setter方法的实现.

         @property 数据类型 名称;

         生成为:

         - (void)set首字母大写的名称:(数据类型)名称;

         - (数据类型)名称;

@property int age;
- (void)setAge:(int)age;
- (int)age;

 3. 使用@property注意.

     1). @property的类型和属性的类型一致.

         @property的名称和属性的名称一致(去掉下划线)

         不要乱写.

     2). @property的名称决定了生成的getter和setter方法的名称.

        所以,@property的名称要和属性的名称一致 去掉下划线  否则生成的方法名就是不符合规范的

        @property的数据类型决定了生成的setter方法的参数类型 和 getter方法的返回值类型.

     3). @property只是生成getter和setter方法的声明. 实现还要自己来. 属性还要自己定义.

     

 08-@synthesize

 1. @property 只能生成getter和setter的声明.

     实现还要我们自己来.

     而实现也是没有什么任何技术含量. 方法实现的代码能不能也可以自动生成呢?

 2. @synthesize

     1).作用: 自动生成getter、setter方法的实现.

        所以,应该写在类的实现之中.

     2).语法:

         @synthesize @property名称;

@interface Person : NSObject
{
    int _age;
}
@property int age;
@end

@implmentation Person

@synthesize age;

@end

     3).@synthesize做的事情.

@implmentation Person

@synthesize age;

@end

         

@implementaion Person
{
    int age;
}

- (void)setAge:(int)age
{
    self->age = age;
}
- (int)age
{
    return age;
}
@end

         a.  生成1个真私有的属性.属性的类型和@synthesize对应的@property类型一致.

         属性的名字和@synthesize对应的@property名字一致.

         b.  自动生成setter方法的实现.

         实现的方式: 将参数直接赋值给自动生成的那个私有属性.并且没有做任何的逻辑验证.

         c.  自动生成getter方法的实现.

         实现的方式: 将生成的私有属性的值返回.

 3. 希望@synthesize不要去自动生成私有属性了.

     getter setter的实现中操作我们已经写好的属性就可以了.

     语法:

         @synthesize @property名称 = 已经存在的属性名;

        

@synthesize age = _age;

     1). 不会再去生成私有属性.

     2). 直接生成setter getter的实现,

         setter的实现: 把参数的值直接赋值给指定的属性.

         gettter的实现: 直接返回指定的属性的值.

 4. 注意:

     1). 如果直接写1个@synthesize(自动生成私有属性)

        @synthesize name;

     2). 如果指定操作的属性.

        @synthesize name = _name;

     3). 生成的setter方法实现中 是没有做任何逻辑验证的 是直接赋值.

        生成的getter方法的实现中 是直接返回属性的值.

        如果setter或者getter有自己的逻辑验证 那么就自己在类的实现中重写就可以了.

 5. 批量声明

    1). 如果多个@property的类型一致. 可以批量声明.

@property float height,weight;

    2). @synthesize也可以批量声明.

@synthesize name = _name,age = _age,weight = _weight,height = _height;

 09-@property增强

  1. @property & @synthesize 做啥:

@property只是生成getter  setter 的声明.   

@synthesize是生成getter  setter 的实现.

    这种写法是Xcode4.4之前的写法. 从Xcode4.4以后.Xcode对@property做了1个增强

  2. @property增强

    只需要写1个@property 编译器就会自动

     1) 生成私有属性.

     2).生成getter setter的声明.

     3).生成getter setter的实现.

@property NSString *name;

     做的事情

     1). 自动的生成1个私有属性,属性的类型和@property类型一致 属性的名称和@property的名称一致 属性的名称自动的加1个下划线.

     2). 自动的生成这个属性的getter setter方法的声明

     3). 自动的生成这个属性的getter setter方法的实现.

     setter的实现: 直接将参数的值赋值给自动生成的私有属性.

     getter的实现: 直接返回生成的私有属性的值.

 3.使用注意.

     1). @property的类型一定要和属性的类型一致.

        名称要和属性的名称一致 只是去掉下划线.

     2). 也可以批量声明相同类型的@property

     3). @property生成的方法实现没有做任何逻辑验证.

         setter: 直接赋值

         getter: 直接返回.

         所以,我们可以重写setter来自定义验证逻辑.如果重写了setter 还会自动生成getter

         如果重写了getter 还会自动生成setter

         如果同时重写getter setter 那么就不会自动生成私有属性了.

 4. 如果你想为类写1个属性 并且为这个属性封装getter setter,1个@property就搞定.

 5. 继承.

    父类的@property一样可以被子类继承.

    @property生成的属性是私有的 在子类的内部无法直接访问生成的私有属性。

    但是可以通过setter getter来访问

 10-动态类型和静态类型

  1. OC是1门弱语言.

     编译器在编译的时候.语法检查的时候没有那么严格.

     不管你怎么写都是可以的.

     int num = 12.12;

     优点: 灵活 咋个写都行.

     缺点: 太灵活

     强类型的语言: 编译器在编译的时候 做语法检查的时候 行就是行 不行就是不行.

 2. 静态类型:

     指的是1个指针指向的对象是1个本类对象.

     动态类型:

     指的是1个指针指向的对象不是本类对象.

3. 编译检查.

     编译器在编译的时候,能不能通过1个指针去调用指针指向的对象的方法.

     判断原则: 看指针所属的类型之中是有这个方法,如果有就认为可以调用 编译通过.

     如果这个类中没有 那么编译报错.

     这个叫做编译检查. 在编译的时候 能不能调用对象的方法主要是看指针的类型.

     我们可以将指针的类型做转换,来达到骗过编译器的目的.

 4. 运行检查.

    编译检查只是骗过了编译器. 但是这个方法究竟能不能执行.

    所以在运行的时候.运行时会去检查对象中是否真的有这个方法.如果有就执行 如果没有就报错误.

 5. LSP

    父类指针指向子类对象.

    实际上任意的指针可以执行任意的对象.编译器是不会报错的.

    当1个子类指针执行1个父类对象的时候,编译器运行通过子类指针去调用子类独有的方法.

    但是在运行的时候是会出问题的.因为父类对象中根本没有子类成员.

 11-id类型

 1. NSObject.

     是OC中所有类的基类.根据LSP NSObject指针就可以指向任意的OC对象.

     所以.NSObject指针是1个万能指针.可以执行任意的OC对象.

     缺点: 如果要调用指向的子类对象的独有的方法.就必须要做类型转换.

 2. id指针.

    是1个万能指针,可以指向任意的OC对象.

     1). id是1个typedef自定义类型 在定义的时候已经加了*

        所以,声明id指针的时候不需要再加*了.

     2)  id指针是1个万能指针,任意的OC对象都可以指.

     

 3. NSObject和id的异同.

     相同点: 万能指针 都可以执行任意的OC对象.

     不同点: 通过NSObject指针去调用对象的方法的时候.编译器会做编译检查.

     通过id类型的指针去调用对象的方法的时候,编译器直接通过.无论你调用什么方法.

     注意: id指针只能调用对象的方法  不能使用点语法.如果使用点语法就会直接报编译错误 。

     如果我们要声明1个万能指针 千万不要使用NSObject 而是使用id

 4. 父类中的类方法创建1个父类对象返回.

     1). 如果返回值写为父类类型的.那么子类来调用这个方法得到的就是父类指针.

     解决的方式: 把返回值改为id类型的.

     2). 方法的内部创建的对象的是 不要写死. 因为写死创建的对象就固定了.

     我们希望那1个类来调用这个方法就创建那1个类的对象.

     把类名写为self 那1个类来调用这个方法 self就指的是那1个类.创建的就是那1个类的对象.

     3). 方法的返回值是id类型的.问题就是任意指针都可以接收这个方法的返回值.

     编译器连个警告都没有.

     如果方法的返回值是instancetype

     代表方法的返回值是当前这个类的对象.

 5. 使用建议

     1). 如果方法内部是在创建当前类的对象,不要写死成类名 【类名 new】;

        而是用self代替类名.

     2). 如果方法的返回值是当前类的对象,也不要写死了. 而是写instancetype

 6. id和instancetype的区别.

     1).instancetype只能作为方法的返回值.不能在别的地方使用.

     id既可以声明指针变量 也可以作为参数 也可以作为返回值.

     2). instancetype 是1个有类型的 代表当前类的对象.

     id是1个无类型的指针 仅仅是1个地址.没有类型的指针.

12-动态类型检测

 1. 编译检查.

    苹果编译器的名字: LLVM

     编译器在编译的时候. 判断1个指针是否可以调用指向的对象的方法.

     判断的准则就是指针的类型.

     我们可以很轻松的把编译器给骗过.

 2. 就算骗过了编译器,程序在运行的时候还会做运行检查.

    我们写的程序就算编译通过了.不意味着可以完美的执行.

 3. 我们就希望.我们可以写代码来先判断1下.对象中是否有这个方法.如果有再去执行.

    如果没有就别去执行.

     1). 判断对象中是否有这个方法可以执行.

- (BOOL)respondsToSelector:(SEL)aSelector;
    Student *s1 = [Student person];
    BOOL b1 = [s1 respondsToSelector:@selector(lenth)];
    if (b1 == YES) {
        [s1 setName:@"王铁锤" andAge:30];
    }
    else{
        NSLog(@"没有方法");
    }

        2). 判断指定的对象是否为 指定类的对象或者子类对象.

- (BOOL)isKindOfClass:(Class)aClass;

        BOOL b1 = [s1 isKindOfClass:[Person class]];

    Student *s1 = [Student person];
    [s1 setName:@"王铁锤" andAge:30];
    
    BOOL b1 = [s1 isKindOfClass:[Person class]];
    if (b1 == YES) {
        NSLog(@"name:%@  age:%d",s1.name,s1.age);
    }
    else{
        NSLog(@"不是Person类");
    }

        判断b1对象是否为Student对象或者Person的子类对象.

     3). 判断对象是否为指定类的对象 不包括子类.

- (BOOL)isMemberOfClass:(Class)aClass;
    Student *s1 = [Student person];
    [s1 setName:@"王铁锤" andAge:30];
    
    BOOL b1 = [s1 isMemberOfClass:[Student class]];
    if (b1 == YES) {
        NSLog(@"name:%@  age:%d",s1.name,s1.age);
    }
    else{
        NSLog(@"不是Student类");
    }

        判断b1对象是否为1个Student对象. 不包括Student的子类对象.

     4). 判断类是否为另外1个类的子类.

+ (BOOL)isSubclassOfClass:(Class)aClass;
    Student *s1 = [Student person];
    [s1 setName:@"王铁锤" andAge:30];
    
    BOOL b1 = [Student isSubclassOfClass:[Person class]];
    if (b1 == YES) {
        NSLog(@"name:%@  age:%d",s1.name,s1.age);
    }
    else{
        NSLog(@"不是Student类");
    }

     

 最常用的是这个方法. 

 判断类中是否有指定的类方法.

+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
    Student *s1 = [Student person];
    [s1 setName:@"王铁锤" andAge:30];
    
    BOOL b1 = [Student instancesRespondToSelector:@selector(setName:andAge:)];
    if (b1 == YES) {
        NSLog(@"name:%@  age:%d",s1.name,s1.age);
    }
    else{
        NSLog(@"不是Student类");
    }

13-构造方法

 1. 创建对象,我们之前说:

     类名 *指针名 =  [类名  new];

     new实际上是1个类方法.

     new方法的作用:

     -> 创建对象。

     -> 初始化对象

     -> 把对象的地址返回.

     new方法的内部,其实是先调用的alloc方法. 再调用的init方法.

     alloc方法是1个类方法,作用: 那1个类调用这个方法 就创建那个类的对象,并把对象返回.

     init方法 是1个对象方法,作用: 初始化对象.

     创建对象的完整步骤:

     应该是先使用alloc创建1个对象,然后再使用init初始化这个对象 才可以使用这个对象.

     虽然没有初始化的对象 有的时候 也可以使用. 但是千万不要这么做.

     使用1个未经初始化的对象是极其危险的.

     Person *p1 = [Person new];

     完全等价于

     Person *p1 = [[Person alloc] init];

     

 2. init方法.

     作用: 初始化对象,为对象的属性赋初始值 这个init方法我们叫做构造方法.

     init方法做的事情:初始化对象.

     为对象的属性赋默认值.

     如果属性的类型是基本数据类型就赋值为0

     C指针           NULL

     OC指针          nil

     所以.我们创建1个对象如果没有为这个对象的属性赋值 这个对象的属性是有默认值的.

     所以,我们每次新创建1个对象,这个对象的属性都被初始化了.

 3. 我们想要让创建的对象的属性的默认值不是 nil NULL 0

     而是我们自定义的.

     那么这个时候,我们就可以重写init方法. 在这个方法中按照我们自己的想法为对象的属性赋值.

     

     重写init方法的规范:

     1). 必须要先调用父类的init方法.然后将方法的返回值赋值给self

     2). 调用init方法初始化对象有可能会失败,如果初始化失败.返回的就是nil

     3). 判断父类是否初始化成功. 判断self的值是否为nil 如果不为nil说明初始化成功.

     4). 如果初始化成功 就初始化当前对象的属性.

     5). 最后 返回self的值.

     解惑

     1). 为什么要调用父类的init方法.

     因为父类的init方法 会初始化父类的属性. 所以必须保证当前对象中的父类属性也同时被初始化.

     2). 为什么要赋值给self?

     因为.调用父类的init方法 会返回初始化成功的对象

     实际上返回的就是当前对象。但是我们要判断是否初始化成功.

     无论如何,记住重写init方法的规范.

- (instancetype)init
{
    if(self = [super init])
    {
     //初始化当前类的属性的代码;
    }
    retrun self;
}

     什么时候需要重写init方法:

     如果你希望创建出来的对象的属性的默认值不是 nil NULL 0 而是我们指定的值.

     那么这个时候我们就可以重写init方法.

 3.  重写init方法以后.

     稍稍不爽的: 这样每次创建出来的对象的属性的值都是一样的.

     创建对象的时候,对象的属性的值由创建对象的人来指定.而不是写死在init方法中

     自定义构造方法.

     规范:

     1). 自定义构造方法的返回值必须是instancetype

     2). 自定义构造方法的名称必须以initWith开头.

     3). 方法的实现和init的要求一样.

     什么时候要自定义构造方法?

        如果希望在构造对象的同时就指定对象的同时。 .....

*

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值