OC基础DAY05 - 点语法和构造方法

来源:互联网 发布:淘宝怎么增加权重 编辑:程序博客网 时间:2024/05/16 15:12

        • 点语法
        • property竟然是Xcode44之前的这个只是了解不能用讲完才说
        • synthesize也是44之前的
          • 使用注意
        • property增强 开发就用这种
            • 从Xcode44开始苹果对property做了增强
    • 有自己的逻辑验证就重写其中一个或者全部重写
  • 动态类型和静态类型
        • 任何指针可以指向任何的对象
    • 强类型语言的缺点是不自由优点是可以提前发现错误
            • OC是一门动态语言运行的时候才能确定一切
        • 编译检查
        • 运行检查
            • 有时候你骗过了编译器是没用的
            • 就算你逃过了编译器也一定逃不过运行时但如果对象中只要有同名的方法就不会报错
        • 万能指针 NSObject指针
            • 根据我们的里氏替换原则NSObject类是所有的OC类的祖宗类基类
        • id指针
            • id类型是一个typedef类型的在typedef的时候已经加了
        • id指针总结考点
    • 运行的时候仍然要做运行检查
  • instancetype
        • 在对象方法中使用self创建当前类的对象
        • 动态类型检测
          • 编译检查和运行检查
  • 构造方法
        • 自定义构造方法
        • 快速创建对象方法

点语法

  1. 如果要访问对象的属性,还要去调用属性对应的setter getter方法,好烦躁
  2. 点语法的作用:快速调用属性的setter和getter方法.
  3. 语法
对象名.去掉下划线的属性名;p1.name = @"小明";NSString *name = p1.name;如果是赋值,就自动调用这个属性对应的setter方法.如果是取值,就自动调用这个属性对应的getter方法.
  • 使用点语法快速调用setter方法为属性赋值.
原理HMPerson *p1 = [HMPerson new];p1.name = @"小明"; //不是把@"小明"直接赋值给p1对象的_name属性原理:==编译器在编译的时候==,其实就把点语法替换为了调用setter方法的代码替换规则:对象名.去掉下划线的属性名 = 数据;替换成:[对象名 set去掉下划线的属性名并首字母大写:数据];p1.name = @"小明";[p1 setName:@"小明"];所以使用点语法为属性赋值本质之上还是调用setter方法.
  • 使用点语法快速调用getter方法取出属性的值
格式 : 对象名.去掉下换线的属性名;NSString *name = p1.name;本质上是调用p1对象的_name属性的getter方法.取到的值是getter方法的返回值.


  • 点语法是一个编译器特性
    注意!!:

点语法是一个编译器特性.原理:==编译器在编译的时候==,其实就把点语法替换为了调用setter(getter)方法的代码!

  • 几个使用注意
    1. setter getter方法内如果使用点语法赋值,就容易无限递归,死循环!慎用!!
    2. 如果属性没有封装setter getter自然是掉用不了点语法的.
    3. 点语法是在编译器编译的时候转换位对应的set get.如果方法名字不符合规范也是无法使用的.
    4. 点语法不是访问成员变量,而是方法的调用.->才是访问属性.

  • 5. 在等号的左边就是赋值,set. 在等号右边或者没有等号就是取值.调用get.


    @property����竟然是Xcode4.4之前的,这个只是了解.不能用!!!讲完才说

    1. 作用: 自动生成getter setter方法声明 写在interface中
    2. 语法
    @interface{    NSString *_name;}@property 数据类型 去掉下划线名称;@property NSString* name;原理: 在编译器编译的时候.会根据@property自动生成getter setter方法的声明.生成方法的规则:a. 先生成setter方法的声明- (void)set名称首字母大写并去掉下划线:(数据类型)去掉下划线名称;b. 再生成getter方法的声明.- (数据类型)去掉下划线名称;@property NSString *name;- (void)setName:(NSString *)name;- (NSString *)name;
    1. 使用注意
      1. @property的类型要和属性的类型一致
      2. 名称要和属性名称一致(去掉下划线)
      3. 这样才能生成符合规范的的getter setter
      4. 批量声明����开发式可不要用哦~

    @property NSString * name,stuNum;当property的类型相同的时候可以批量声明
    1. @property只是生成声明,实现还是要自己写.

    @synthesize��也是4.4之前的

    1. 刚才将的自动声明,如果手动去实现有一种被羞辱的感觉,太简单了~
    2. 方法的实现既然如此简单,可以自动写出来吗?当然可以
    3. 作用自动生成getter setter实现
    4. 所以写在@implementation中
    5. 使用格式
    @synthesize @pro名称;@synthesize name;注意,这个@synthesize后面的名称必须要是@interface@property中的名称一定是前面已经声明过的syn异步
    1. @synthesize原理
    interfae HMPerson : NSObject{    NSString *_name;}@property NSString *name;@end@implementation HMPerson@synthesize name;//@synthesize做的事情//编译器在编译的时候//a. 先自动生成一个私有属性,类型和@synthesize对应的@pro类型一致//名称和@pro的名称一致,不带下划线//这个属性是一个真私有属性,也就是声明在@implementation里面.{    NSString *name;}//b. 自动的生成setter方法的实现.//方法的内部什么都没做,直接将参数赋值给了它自动生成的属性.- (void)setName:(NSString *)name{    //私有属性name和形参name同名 必须用self->    self->name = name;}//c. 自动生成getter方法//直接返回生成的私有属性的值- (NSString *)name{    return name;}
    • 虽然@synthesize会自动生成属性
      1. 但是生成属性的名称是很不符合规范的
      2. 所以希望他不要去自动生成私有属性了.
      3. 同时区操作已经存在的_name;
    语法@synthesize @pro名称 = 已经存在的属性名@synthesize name = _name;1. 代表@synthesize 不会再去生成私有属性了.2. 自动生成setter方法的实现.3. 直接将参数的值赋值给后面指定的属性.4. 自动生成getter方法的实现,直接返回后面指定的属性的值.- (void)setName:(NSString *)name{    _name = name;}- (NSString *)name{    return _name;}
    使用注意
    • 实现
      1. @synthesize生成的方法实现没有逻辑判断,直接赋值和返回.
      2. 如果你有自己的逻辑判断就自己重写.
      3. 如果重写其中一个实现,另一个还是会自动生成.
    • 批量实现
    @synthesize name = _name , age = _age , stuNum = _stuNum;类型不同也可以批量.

    ==老项目中老框架中会有这样的写法.要看懂!==

    @property增强 开发就用这种

    从Xcode4.4开始苹果对@property做了增强
    • 只要些一个@property 编译器就会自动的帮助你完成如下工作 ==(面试题)==
      1. 自动帮助你生成一个带下划线的,类型和@pro类型一致的真私有属性
      2. 自动帮你生成这个属性的getter setter 方法声明
      3. 自动帮你生成这个属性的getter setter 方法实现
    • @property原理
    @interface HMPerson : NSObject//只写这一个@property 就有很多代码产生@property NSString *name;@end@implementation HMPerson@end----------编译后---------@interface HMPerson : NSObject- (void)setName:(NSString *)name;- (NSString *)name; @end@implementation HMPerson{    NSString *_name;}- (void)setName:(NSString *)name{    _name = name;}- (NSString *)name{    return _name;}@end
    • 使用@property的注意
      1. 怎么写?
    @property 想要生成的属性的类型 想要生成的名称一致并去掉下划线.
    1. 批量声明
    @property int num1,num2,num3;//前提是类型相同的情况下.
    1. @property生成的方法实现是没有任何的逻辑验证的.
    如果需要可以自己重写- (void)setName:(NSString *)name{    if(name.length < 2)    {        _name = @"无名";    }    else    {        _name = name;    }}1. 如果你重写了setter方法,仍然会自动生成getter方法.反之亦然.2. 但自动生成的是没有逻辑验证的3. 如果你同时重写了两个方法,@property就不会再自动生成那个私有属性了.只能自己加
    • 从今往后我们再也不会去写一个属性的声明以及getter setter方法.除非
      1. 属性不需要被外界方位.就自己写真私有

    2. 有自己的逻辑验证,就重写其中一个或者全部重写.

    动态类型和静态类型

    任何指针可以指向任何的对象

    • OC是一门弱类型语言
      1. 编译器在检查语法的时候,检查的没有那么严格.你怎么写编译器都编译通过.只要不要太过分
    1. 数据类型不一样可以强行赋值int num = 10.12.这样只有警告,居然还能打印出来.HMPerson *p1 = @"jack";3. 警告,而且也可以%@打印NSString *str = [HMPerson new];
    • 强类型语言 Java C#…

      1. 编译器在检查语法的时候非常的严格
      2. 是怎样就是怎样,一编译就报错.
    • 弱语言的优缺点

      1. 优点是自由灵活,想咋写就咋写
      2. 缺点是出错了之后都不知道错在哪.错误只有在运行的时候发生.

    3. 强类型语言的缺点是不自由,优点是可以提前发现错误.

    • 动态类型和静态类型
    OC是一门动态语言.运行的时候才能确定一切
    1. 静态类型
    HMPerson *p1 = [HMPerson new];指针指向的对象是一个本类对象
    1. 动态类型就是指针指向的不是一个本类对象

      > HMPerson *p1 = [HMStudent new]
      > //指向的是子类对象或者@"jack";
      >

      1. 所以OC中任意的指针可以指向任意的对象.编译器最多给个警告
      2. 当指向的是一个本类对象或者子类对象的时候,编译器不会给警告.
      3. 因为要存储一个对象的地址,其实任意类型的指针变量都不所谓,任何类型指针都是8个字节.

    编译检查

    1. 编译检查是编译器在编译源代码的时候主要是检查代码是否符合语法规范.(Xcode是LLVM)
    2. 编译器在编译的时候如何确定这个代码有没有语法错误呢?
    3. 通过一个指针去调用一个方法时能不能调用编译器是如何判断的呢?
    [对象名 方法名]; //这句话能不能通过编译,编译器的判断准则是什么?[p1 sayHi];检查对象(p1)的类(HMPerson)中有没有这个方法(sayHi),如果有就编译通过,没有就编译报错NSString *str = [HMPerson new][str length];检查NSString 中 有没有length方法.有就通过.HMPerson *p1 = [HMStudent new];[p1 sayHi];//可以编译[p1 study];//person类型没有这个方法,但是student有,所以编译仍然不能通过.[(HMStudent*)p1 study]; //可以通过强制类型转换骗过编译器

    运行检查

    有时候你骗过了编译器是没用的.
    NSString *str = [HMPerson new];[str study];//编译肯定报错. [(HMStudent*)str study];//编译通过运行还是会崩1. 程序在运行的时候,当要调用一个指针指向的方法的时候,仍然会做一个检查.2. 会去检查这个指针指向的对象当中是否真的有这个方法,3. 如果真的有就执行,如果没有就直接运行报错[str length];//还是崩.这样崩了就是运行检查.
    • 总结两点
      1. 编译检查只是检查指针的类型.
      2. 运行检查才会真正的去检查对象当中是否有这个方法.
    就算你逃过了编译器也一定逃不过运行时,但如果对象中只要有同名的方法就不会报错.!!!

    万能指针 NSObject指针

    根据我们的里氏替换原则,NSObject类是所有的OC类的祖宗类(基类);
    • 所以,NSObject指针可以指向任意的OC对象.
    NSObject *obj = [HMPerson new];obj = @"jack";obj = [NSArray new];//C字符串不行!基础数据类型不行!obj = "rose"obj = 12;obj = YES;//YES就是1 也不行![obj sayHi];//不行 NSObject类型没有sayHi[(HMPerson*)obj sayHi]; //可以

    当NSObject指针指向子类对象的时候,如果要调用子类对象的独有成员,就必须要做类型转换.

    NSObject *obj = @"jack";//obj指针指向的是一个NSString对象[obj length];//报错,NSObject类型没有length方法[(NSString*)obj length];//强制转换就行了.NUUInterger len = [(HMPerson *)obj length];NSLog(@"len = %lu",len);注意易错点点!!1. 编译的时候检查person有length方法.2. 运行的时候运行字符串对象@"jack"中的length方法.得到的结果是4!
    • 所以
      1. NSObject指针是一个万能指针.
      2. 当指针的类型是NSObject*类型的时候,编译器要做编译检查.

    id指针

    id类型是一个typedef类型的.在typedef的时候已经加*了
    • id指针是一个万能指针,它可以指向任意的OC对象.
    id id1 = @"jack";id id2 = [HMPerson new]; id id3 = 12;//不可以NSObject*一样是万能指针
    • 和NSObject*的区别
      1. 当类型是NSObject*类型是编译器会做编译检查.
      2. 当指针的类型是id类型的时候,不会做编译检查直接通过.
    1. 当编译器在编译的时候如果发现指针的类型是id类型的
    2. 这个时候,直接通过,不会做任何的编译检查.不需要强转
    • id指针的局限性:==不能使用点语法只能调用方法==
    id id1 = [HMPerson new];[id1 sayHi]; //可以运行id1.name = @"jack";//不行,虽然有set方法.[id1 setName:@"jack"];//可以

    id指针总结(考点)

    1. NSBject指针和id指针都是叫做万能指针,他们都可以指向任意的OC对象.
    2. 通过指针去调用方法的时候
      1. 如果指针的类型是NSObject类型的时候,编译器在编译的时候会做编译检查.需要做类型转换
      2. 如果是id类型编译器在编译的时候就会直接通过.不用做类型转换.

    3. 运行的时候仍然要做运行检查.

    instancetype

    1. 之前学的在父类当中写一个类方法返回一个纯洁的对象.
    2. 子类可以继承这个类方法,通过子类也可以调用这个方法.返回值是父类的对象.
    HMPerson *p1 = [HMPerson person];HNStudent *s1 = [HMStudent person];//继承以后有person方法但是会报警告,用一个Student接person.
    • 如何解决呢?
      1. 返回值写一个万能指针
    + (id)person{    return [HMPerson new];}就算接返回值的类型不对也不会给警告,因为id不会做检查.但是方法里面本来就是创建的person对象,
    1. 虽然没有警告了,但是无论通过哪个类去调用返回的都是person对象.
    2. 希望哪个类型调用就创建哪个类的对象
    + (id)person{    //返回一个调用这个类方法的对象    return [self new];}

    在类方法中创建对象的时候类名不要写死了,而是写一个self代表当前类

    1. 还存在的问题
    //现在就不会报类型不匹配的警告了因为是id类型NSString *str = [HMPerson person];NSString *str1 = [HMStudent person];1. 这个时候,要接收这个类方法返回的对象的地址,实际上任意类型的指针都可以2. 编译器都不会报警高,因为id类型不会做编译检查,id是一个无类型指针.

    希望类方法通过哪一个类型调用,返回值类型就是那个对象类型.有警告提示!

    1. 将方法的返回值写乘instancetype
      这个方法通过哪一个类去调用,就代表返回的是哪一个类的对象.返回值有类型
    + (instancetype)person{    return [self new];}

    这个才是终极写法.创建的是调用类的对象,返回值是调用类的类型.

    • instancetype 和 id的区别 ( 面试题 )
      1. instancetype只能作为方法的返回值,别的地方不能使用.
      2. id是1个万能指针,不仅可以作为方法的返回值,也能声明id类型变量,也可以作为方法的参数.

    在对象方法中使用self创建当前类的对象.

    调用当前对象的class方法.id obj = [self.class new];//点语法换成调用方法id obj = [[self class] new];

    动态类型检测

    编译检查和运行检查

    就算通过了编译检查,运行不一定会成功.

    • 需求:
      编译检查只是检查指针的类型,我们希望检查一下这个指针指向的这个对象当中到底有没有这个方法
    BOOL res = [str respondsToSelector:@selector(length)];//判断指针对象的方法中是不是有SEL发送的指定的方法.res == YES //就执行
    ==可以判断对象中有没有这个方法,有没有实现== 给五颗星
    • isKIndOfClass==四颗星==
    BOOL res = [p1 isKindOfClass:[HMPerson class];//判断s1对象是不是person的对象或者是不是其子类对象
    • isMenberOfClass: (Class)aClass

      只能判断本类对象不包括子类

    • isSubclassOfClass: (Class)aClass;

      这个是类方法 判断一个类是不是另一个类的子类.

    构造方法

    1. 创建一个类的对象,那么就调用这个类的new方法就可以
      1. new方法是一个类方法,哪个类调用就创建哪个类的对象,并且初始化,返回值是这个对象地址.
      2. new方法内部什么都没做,而是调用了alloc方法和init方法
      3. alloc方法首先是一个类方法,这个方法做的事情:创建对象
      4. init方法是对象方法,做的事:初始化这个对象.为对象的属性赋默认值.
      5. 创建对象我们也可以这么做.

    [HMPerson new] == [[HMPerson alloc] init];1. 这两种完全等价2. 开发中多用这种方法更专业3. init方法 我们叫做构造方法4. init方法不仅仅为对象赋默认值还有别的事情5. 创建的对象务必要调用init方法初始化以后才可以使用,否则就有可能会出问题!
    • init叫做构造方法 ==一定是对象方法,一定要注意(系统的方法)==

      我们的快速创建对象一定是类方法.

      1. init方法就是我们的构造方法

      为什么对象一创建出来,对象的属性的默认值就是0,nil,NULL,因为构造方法中初始化了

    • 如果我们希望对象初始化以后不是默认值而是别的,那我们就重写init方法
    - (instancetype)init{    //1. 先调用父类的init方法,因为父类的init方法不仅仅是初始化属性的值,还做了别的重要的事情    //2. 返回的值是当前的对象,调用init方法有可能执行失败返回nil,将返回值赋给self,再判断有没有执行成功    self = [super init];    //3. 判断self有没有初始化成功,再去按照自己的方式赋值    if(self != nil)//说明父类的init方法执行成功    {        self.name = @"小明";        self.age = 17;        self.height = 176.6f;    }    //4. 最后返回当前对象    return self;}
    • 总结
      1. 什么时候需要重写init方法?
        1. 对象创建出来不希望属性默认值是0 nil,而是指定的值.
        2. 如果你有一段代码想在对象创建的同时执行,也可以把代码写在init方法里面.
        3. 重写init方法的规范 必须要调用父类的init方法,并且赋值给self判断成功.

    if(self = [super init])//一般直接这么写.判断

    自定义构造方法

    1. 重写init方法以后. 创建出来的所有的对象属性都一样.
    2. 需求:对象的属性的值 由用户传进来
    3. 自定义构造方法
      1. 肯定是对象方法
      2. 返回值写 instancetyper
      3. 方法名必须以initWith开头

    - (instancetype)initWithName:(NSString *)name andAge:(int)age andHeight:(float)height{    if(self = [super init])    {        self.name = name;        self.age = age;        self.height = height;    }    return self}调用:HMPerson *p2 = [[HMPerson alloc] initWithName:@"jack" andAge:18 andHeight:176.7f;
    1. 自定义构造方法一定要是init或者initWith开头W区分大小写
    2. ==因为self只能在构造方法中赋值==!编译器认为init方法或者以iniiWith开头的方法才是构造方法.

    快速创建对象方法 *

    + (instancetype)personWithName:(NSString *)name andAge:(int)age{    return [[self alloc]initWithName:@"name" andAge:age];}
    0 0