OC基础学习-03

285 阅读9分钟
  • @property:编译器指令,在Xcode4.4之前,可以使用@property来代替getter/setter方法的声明,也就是说我们只需要写上@property就不用写getter,setter的声明,Xcode之后增强
@property int age;
  • @syntheszie:编译器指令,可以简化getter/setter方法的实现
    • 在@sythesize后面告诉编译器,需要实现哪property生成的声明
    • 告诉@sythesize,需要将传入的值赋值给谁和返回谁的值给调用者
    • 如果再@sythesize后面没有告诉系统将传入的值赋值给谁,系统默认会赋值给和@sythesize后面写的名称相同的成员变量
@syntheszie age = _age;
  • 从Xcode4.4之后,对@property进行了一个增强,以后只需要一个@property就可以同时生成setter/getter的声明和实现,如果没有告诉@property要将传入的参数赋值给谁,默认会将传入的属性赋值给——开头的成员变量
    • 弊端:只会生成最简单的getter、setterd声明和实现,不会对数据进行过滤,必须自己重写
    • 如果重写了setter方法,那么property就只会生成getter方法
    • 如果重写了getter方法,那么property就只会生成setter方法
    • 如果利用property来生成getter/setter方法,那么我们可以不屑成员变量,系统会自动给我们生成一个_开头的成员变量,是私有的成员变量(在.m生成的)
    • 如果同时重写了getter/setter方法,那么property就不会自动帮我们生成私有的成员变量
  • property修饰符
    • 如果给一个属性同时提供了getter/setter方法,那么我们称这个属性为可读可写属性 readwrite(默认)
    • 如果只提供了getter方法,那么我们称这个属性为只读属性 readonly
    • 如果只提供了setter方法,那么我们称这个属性为只写属性 没有修饰符
    • 如果既没有提供setter也没有提供getter方法,那么我们称这个属性为私有属性
@property(readonly) NSString  *name;
@property(readwrite) NSString *name;
@propertygetter=abc) NSString *name; 重新命名getter方法名称,用的多
@propertysetter=abc) NSString *name; 重新命名setter方法名称
  • id:动态数据类型,编译时并不知道真实类型,只有在运行时才会知道其数据类型,如果访问了不属于动态类型的属性和方法,编译器不会报错
    • 定义变量
    • 作为函数的参数
    • 作为函数的返回值
    • 弊端:由于动态类型可以调用任意方法,所以有可能调用到不属于自己的方法,而编译时又不会报错,所以可能运行时出错
    • 一般用在多态,可以减少代码量,比卖你调用子类特有的方法需要强制类型转换
  • 默认情况下所有的数据类型都是静态数据类型,在编译时就知道变量的类型,属性方法,在编译时就可以访问这些属性方法,并且如果访问了不属于静态数据类型的方法和属性时,编译就会出错
  • id== NSObject * 万能指针,区别:id是动态数据类型,NSObject *是静态数据类型
  • 通过静态数据类型自定义对象,不能调用子类特有方法
  • 通过动态数据类型定义的变量,可以调用子类特有的方法
  • 通过动态数据类型定义的变量,可以调用私有的方法
  • 为了避免动态数据类型引发的运行时错误,一般情况下如果使用动态数据类型定义一个变量,在调用这个变量的方法之前会进行一次判断吗避免错误
id obj = [Person new]
if([obj isKindOfClass:[Person class]]){
 }
  • isKindOfClass :判断指定的对象是某一个类,或者某一个类的子类
  • isMemberOfClass:判断指定的对象是否时当前指定类的实例
  • isSubclassOfClass:判断一个类是不是一个类的子类
  • new做了三件事情
    • 1.开辟存储空间 +alloc方法
    • 初始化所有的属性(成员变量) -init方法(默认情况下什么都没做)
    • 返回对象的地址
  • alloc做的事情
    • 开辟存储空间,堆空间,并将isa指针指向类对象
    • 将所有属性设置为0
    • 返回当前实例对象的地址
  • init方法(构造方法):默认情况下init的实现什么都没有做,返回初始化后的实例对象的地址,该地址和alloc返回地址时一样的
[[Person alloc] init] === [Person new];前者建议,可以统一编码格式

  • 在OC中以init开头的方法,就是构造方法。构造方法的用途:用于初始化一个对象,让某个对象以创建出来就拥有某些属性和值,重写init方法必须按照格式写
1.必须先初始化父类,在出说子类
2.必须判断父类是否初始化完成成功,只有父类初始化成功才能继续初始化子类
3.返回当前对象的地址
- (instancetype) init {
    self = [super inti];//self==nil==0
    if(self){
    //初始化子类
    }
    return self;
}
  • instancetype == id == 万能指针
    • 如果init方法的返回值是instancetype,那么将返回值赋值给一个其他的对象会报一个警告(最开始如果返回类型是id的话,是不会报错的,但是现在会)
    • id可以用来定义变量,可以作为返回值,也可以作为形参
    • instancetype只能作为返回值
  • id在百衲衣是不能判断对象的真实类型
  • instancetype在编译时会判断对象的真实类型(把运行时的错误在编译时就能发现)
  • 自定义构造方法--自定义init方法,W一定要大写,属性名称,方法不要以new开头
    • 一定是对象方法
    • 一定返回id/instancetype
    • 方法名称一定以init开头
- (instancetype) initWithAge:(int) age {
    self = [super inti];//self==nil==0
    if(self){
    //初始化子类
    _age= age;
    }
    return self;
}
  • 类工厂方法:一种用于分配,初始化实例并返回一个它自己的的实例的类方法,(new),简化代码
  • 自定义类工厂方法
    • 一定是+开头
    • 返回值一般是instancetype类型
    • 方法名称以类名开头,首字母小写
    • 注意:但凡自定义类工厂方法,在类工厂方法中创建对象一定不要使用类名来创建,一定要使用self,self在类方法中代表类,谁调用当前方法,self就是哪个类
    [[NSString alloc] initWithString:(NSString *)]; 自定义构造方法
    [NSString stringWithString:(NSString *)];自定义类工厂函数
    
  • 类的本质
    • 类其实也是一个对象,这个对象会在这个类第一次被使用的时候创建
    • 只要有了类对象(保存了属性和对象方法)(也有isa指针指向元类对象(保存了对象的所有类方法),元类对象的isa指针指向根元类对象(NSObject的元类对象),他的isa指向自己),将来就可以通过类对象来创建实例对象
    • 实例对象中有一个isa指针,指向创建自己的类对象
    • 类对象中保存了当前对象所有的对象方法
    • 当给一个实例对象方法发消息的时候,会根据实例对象中的isa指针去对应的类对象中查找
    • 所有类的 类对象 的继承关系就是 元类对象 的继承关系

59b75caafcd3c6bc7d2f4aa405e878da83e31619228895e8dee9c0c831fb18d0QzpcVXNlcnNcZWRpc29cQXBwRGF0YVxSb2FtaW5nXERpbmdUYWxrXDEyNzIyMzMyNTdfdjJcSW1hZ2VGaWxlc1wxNjU5NDk2MDY2NzE2XzVFQTk5OEY1LTMxRUEtNGFiNy1BRUVCLTZFNzY2QjA2QkRFMi5wbmc=.png

  • 获取类对象
    • Class c = [p1 class]
    • 一个类在内存中只有一份类对象
  • 类对象的应用场景
    • 用于创建实例对象
    • 用于调用类方法
    Class c = [p1 class];
    Person * p =[[c alloc] init];
    [c test];
    
  • 类的启动过程
    • 只要程序启动就会将类的代码加载到内存中,放到代码区
    • +load方法会在当前类被加载到内存的时候调用,有且仅会调用一次,如果存在继承关系,会先调用父类的load方法,再调用子类的load方法
    • +initialize 当当前类第一次被使用的时候就会调用(创建类对象的时候),在整个程序的运行过程中只会被调用一次,无论使用多少次这个类都只会调用一次,经常会用到,用于对某一个类进行一次性初始化,如果存在继承关系,会先调用父类的initialize方法,再调用子类的initialize方法
  • SEL类型
    • SEL类型代表着方法的签名,再类对象的方法列表中存储着该签名与方法代码的对应关系
    • 每个类的方法列表都存储再类对象中
    • 每个方法都有一个与之对应的SEL类型的对象
    • 根据一个SEL对象就可以找到该方法的地址,进而调用方法
    • SEL再第一次寻找时会逐个查询,但是同时会缓存,之后的寻找就很快了
    • SEL类型的定义
      • typedef struct objc_selector *SEL
    • [p test]
      • 首先把test这个方法名包装成sel类型的数据
      • 根据SEL数据找到该类的类对象上照,对应的方法的代码,如果找到了就执行该带啊吗
      • 如果没有找到就根据对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码
      • 如果没有找到,一直往上照,知道基类(NSObject)
      • 如果没有找打就报错
    • SEL类型的作用
      • 配合对象/类来检查对象/类中有没有实现某一个方法
      SEL sel = @selector(setAge:);
      Person *p = [Person new];
      //判断p对象中有没有实现-号开头的setAge:方法
      //如果存在返回YES
      BOOL flag = [p respondsToSelector:sel]
      
      
      
      • 配合对象/类来调用某一个SEL方法
       SEL sel = @selector(setAge:);
      Person *p = [Person new];
      //调用p对象中sel类型对应的方法
       [p performSelector:sel] //调用没有参数的方法
       [p performSelector: sel withObject: @"dsadad"]调用有参数的方法,且这个参数必须是对象类型
       [p performSelector:sel withObject:@"dsada " withObject @"dasdsa"] 最多只能接收两个参数
      
      • 配合对象将SEL类型作为方法的形参
      Car *c = [Car new];
      SEL sel = @selector(new);
      Person *p = [Person new];
      [p makeObjcet:c adnSel:sel] //此时p就会调用c对象的new方法
      
      • 的撒
    • resondsToSelector注意点:如果时通过一个对象来调用该方法那么久会判断该对象有没有实现-号开头的方法,如果是通过类来调用该方法,那么久会判断有没有实现+号开头的方法