OC基础--构造方法 id类型

时间:2022-02-19 12:34:30

new方法实现原理:

  new做了三件事情

  1.开辟存储空间  + alloc 方法

  2.初始化所有的属性(成员变量) - init 方法

  3.返回对象的地址

  [Person new]; == [[Person alloc] init];

    alloc: 1.开辟存储空间 2.将所有的属性设置为0 3.返回当前实例对象的地址

    init:  1.初始化成员变量, 但是默认情况下init的实现是什么都没有做 2.返回初始化后的实例对象地址

      注意: alloc返回的地址, 和init返回的地址是同一个地址

构造方法的概念及用途:

  在OC中init开头的方法, 我们称之为构造方法

  用于初始化一个对象, 让某个对象一创建出来就拥有某些属性和值

重写init方法, 在init方法中初始化成员变量:

  重写init方法必须按照苹果规定的格式重写, 如果不按照规定会引发一些未知的错误

    1.必须先初始化父类, 再初始化子类

      子类继承自父类  那么子类拥有父类所有成员  子类必须调用父类的构造方法对这些成员进行初始化

    2.必须判断父类是否初始化成功, 只有父类初始化成功才能继续初始化子类

      为了防止父类的初始化方法release掉了self指向的空间并重新alloc了一块空间。还有[super init]可能alloc失败,这时就不再执行if中的语句

    3.返回当前对象的地址

      super 和 self 指向的是相同的消息接收者 即谁调用就代表谁

      - (instancetype)init
      {
       // 1.初始化父类 只要父类初始化成功 , 就会返回对应的地址, 如果初始化失败, 就会返回nil nil == 0 == 假 == 没有初始化成功
       self = [super init];
       // 2.判断父类是否初始化成功
       if (self != nil) {
       // 3.初始化子类 设置属性的值
       _age = ;
       }
       // 4.返回地址
       return self;
      }

    简版:

      - (instancetype)init
      {
       // 注意: 不要把 = 号写为 == 一定要将[super init]的返回值赋值给self
       if (self = [super init]) {
       // 初始化子类
       _age = ;
       }
       return self;
      }

自定义构造方法:

  自定义构造方法 其实就是自定义一个init方法:

    1.一定是对象方法

    2.一定返回id/instancetype

    3.方法名称一定以init开头

#import "Person.h"

@implementation Person
// 重写init方法
- (instancetype)init{
if (self = [super init]) {
self.name = @"王二小";
self.age = ;
}
return self;
}
// 自定义构造方法 一个类可以有0个或者多个自定义构造方法
- (instancetype)initWithName:(NSString *)name{
if (self = [super init]) {
_name = name;
}
return self;
}
// 自定义构造方法可以有1个或多个参数
- (instancetype)initWithName:(NSString *) name andAge:(int) age{
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}
@end

自定义构造方法在继承中得表现:

  谁声明的成员就由谁去初始化(父类的属性交给父类去处理  子类方法只处理自己独有的属性)

#import "Student.h"

@implementation Student
/*
- (instancetype)initWithStudentNO:(NSString *)studentNO andName:(NSString *)name andAge:(int)age{
if (self = [super init]) {
self.name = name;
self.age = age;
self.studentNO = studentNO;
}
return self;
}
*/
- (instancetype)initWithStudentNO:(NSString *)studentNO andName:(NSString *)name andAge:(int)age{
// 由父类的构造方法去初始化 name 和 age 属性
if (self = [super initWithName:name andAge:age]) {
_studentNO = studentNO;
}
return self;
}
@end

调用图解:

  OC基础--构造方法  id类型

自定义类工厂方法:

  什么是类工厂方法:

    用于快速创建对象的类方法, 我们称之为类工厂方法

    类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间

    自定义类工厂方法是苹果的一个规范, 一般情况下, 我们会给一个类提供自定义构造方法和自定义类工厂方法用于创建一个对象

  规范:

    1.一定是类方法 +

    2.方法名称以类的名称开头, 首字母小写

    3.一定有返回值, 返回值是id/instancetype

   举例:

    [[NSString alloc] init];
[NSString string]; [[NSString alloc] initWithString:(NSString *)];
[NSString stringWithString:(NSString *)]; [[NSArray alloc] init];
[NSArray array]; [NSArray alloc] initWithObjects:(id), ..., nil];
[NSArray arrayWithObjects:(id), ..., nil]; // 自定义类工厂方法
+ (instancetype)personWithName:(NSString *)name andAge:(int)age{
Person * person = [[Person alloc] init];
person.name = name;
person.age = age;
return person;
}

自定义类工厂方法在继承中注意点:

  由于子类默认会继承父类所有的方法和属性, 所以类工厂方法也会被继承

  在类工厂方法中创建对象一定不要使用类名来创建   一定要使用self来创建

    self在类方法中就代表类对象 (谁调用当前方法, self就代表谁)

    父类的类工厂方法创建实例对象时是使用父类的类名创建的, 如果子类调用父类的类工厂方法创建实例对象,创建出来的还是父类的实例对象

@interface Person : NSObject
+ (id)person;
@end @implementation Person
+ (id)person
{
return [[Person alloc] init];
}
@end @interface Student : Person
@property NSString *name;
@end @implementation Student
@end int main(int argc, const char * argv[])
{
Student *stu = [Student person];// 等效于 [[Person alloc] init] 需要该为 [[self alloc] init]
[stu setName:@"lnj"]; // 报错, 因为Person中没有setName
} // 自定义类工厂方法 使用self 而不是 类名
+ (instancetype)personWithName:(NSString *)name andAge:(int)age{
// Person * person = [[Person alloc] init];
Person * person = [[self alloc] init];
person.name = name;
person.age = age;
return person;
}

id类型:

  id是一个数据类型, 并且是一个动态数据类型

  既然是数据类型, 所以就可以用来

    1.定义变量

    2.作为函数的参数

     3.作为函数的返回值

  id == NSObject *   万能指针

  id和NSObject *的区别:

    NSObject *是一个静态数据类型

    id  是一个动态数据类型

  默认情况下所有的数据类型都是静态数据类型

静态数据类型的特点:

  在编译时就知道变量的类型,

  知道变量中有哪些属性和方法

  在编译的时候就可以访问这些属性和方法,

  如果是通过静态数据类型定义变量, 如果访问不了属于静态数据类型的属性和方法, 那么编译器就会报错

动态数据类型的特点:

  在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型

  如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错

  • 通过静态数据类型定义变量, 不能调用子类特有的方法
  • 通过动态数据类型定义变量, 可以调用子类特有的方法
  • 通过动态数据类型定义的变量, 可以调用私有方法
    • 弊端: 由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
    • 应用场景: 多态, 可以减少代码量, 避免调用子类特有的方法需要强制类型转换
  • 为了避免动态数据类型引发的运行时的错误, 一般情况下如果使用动态数据类型定义一个变量, 在调用这个对象的方法之前会进行一次判断, 判断当前对象是否能够调用这个方法
    • isKindOfClass     
        id obj = [Student new];
// isKindOfClass , 判断指定的对象是否是某一个类, 或者是某一个类的子类
if ([obj isKindOfClass:[Student class]]) {
[obj eat];
}  
    • isMemberOfClass
        id obj = [Student new];
if ([obj isMemberOfClass:[Student class]]) {
// isMemberOfClass : 判断指定的对象是否是当前指定的类的实例
[obj eat];
}

instancetype和id的区别:

  instancetype == id == 万能指针 == 指向一个对象

  id在编译的时候不能判断对象的真实类型

  instancetype在编译的时候可以判断对象的真实类型

    (一个在编译时不知道真实类型, 一个在编译时知道真实类型)

  id可以用来定义变量, 可以作为返回值, 可以作为形参

  instancetype只能用于作为返回值

    它会进行类型检查,如果创建出来的对象,赋值了不相干的对象就会有一个警告信息,防止出错

  注意: 以后但凡自定义构造方法, 返回值尽量使用instancetype, 不要使用id