黑马程序员_iOS开发之OC之面向对象之id语法、构造方法、@category分类、类本质、description方法和SEL数据类型

时间:2022-12-10 14:56:04

1、id语法

可以认为id == NSObject *,运行的环境理解为多态,父类指针指向子类对象,万能指针,能指向\操作任何OC对象。

2、构造方法

构造方法可分为:重写系统自带的构造方法,自定义构造方法。

完整地创建一个可用的对象需要下面两步:

       1.分配存储空间  +alloc方法 返回一个已经有存储空间的对象,但是这个对象不可用,因为还没有初始化

       2.初始化 调用上面分配好存储空间的对象的初始化方法-init         

   new方法完成这两步,是new方法内部分别调用两个方法完成这两步。分别是+alloc-init方法

什么是构造方法呢?就相当于init,因为构造方法就一个用途,就是初始化。init默认初始化都为0,如果想让创建的对象初始化为其他值,就要重写父类的init初始化方法。


构造方法:用来初始化对象的方法,是个对象方法,-开头

重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值

   重写构造方法的注意点:

      1.先调用父类的构造方法([super init]

      2.再进行子类内部成员变量的初始化

重写-init方法,重写父类方法不用在头文件.h文件中,再次声明,因为在父类中已经声明了。返回类型用id,因为各种对象类型的都需要初始化,所以用id这个文能指针,可以指向任何对象类型,那么各种类型对象就可以赋值给id类型的变量了。


自定义构造方法,就要声明和实现两步,重写父类构造方法,只要实现即可,因为父类中已经声明了

    自定义构造方法的规范:

 1.一定是对象方法,一定以 -开头

 2.返回值一般是id类型

 3.方法名一般以initWith开头

父类的属性交给父类方法去处理,子类方法处理子类自己的属性(成员变量)。


3、@category分类

 分类是OC特有的,在Java中没有。

分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法。

 使用注意:

 1.分类只能增加方法,不能增加成员变量

 2.分类方法实现中可以访问原来类中声明的成员变量,这里的成员变量不是Xcode自动生成的,是声明的那种,因为自动的是private类型,声明的时protected类型

 3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用,如果有多个category  分类都重新实现了原来类的方法,方面依据编译顺序决定调用谁的方法,谁最后编译就调用谁的方法,这是分类中的                          依据,分类中方法始终比原来类中的方法优先,编译用到是.m源代码文件,.h头文件只是用来拷贝声明的。(删除框                          架时,选中删除索引,不要选择删除到垃圾桶,否则这个框架就被删了。使用局部变量要初始化,全局变量和成员变量                       系统默认初始化为0

 4.方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类 -->父类

4、类的本质

    1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次,因为只加载一次。先加载原始类的+load方法,再加载分类的+load方法

    2.当第一次使用某个类时,就会调用当前类的+initialize方法

    3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)

    先初始化父类,再初始化子类(先调用父类的分类+initialize方法,会覆盖父类的+initialize方法,再调用子类分类的+initialize方法,会覆盖子类的+initialize方法,如果没有分类,就调用父类和子类的+initialize方法)


5、description方法

    -discription方法是NSObject自带的方法

   Person* p = [[Personalloc]init];

    p.age =20;

    p.name =@"jack";

   //默认情况下,利用NSLog%@输出对象时,结果是:<类名:内存地址>

    // 1.会调用对象p-description方法

    // 2.拿到-description方法的返回值(NSString *)显示到屏幕上

    // 3. -description方法默认返回的是类名+内存地址

    NSLog(@"%@", p);//默认输出:<Person: 0x100102c00>,即类对象和地址

    NSString * name =@"rose";//输出: rose NSString类重写了NSObject-discription方法

   NSLog(@"%@", name);


// 决定类实例对象的输出结果

- (NSString *)description

{

    // NSLog(@"%@", self); //会造成死循环

    //stringWithFormat:拼字符串函数

    return [NSStringstringWithFormat:@"age=%d, name=%@",_age,_name];

}

// 决定类对象的输出结果

+ (NSString *)description

{

    // NSLog(@"%@", self); //会造成死循环

    //stringWithFormat:拼字符串函数

   return@"ABC"; 

}


6、SEL类型

    SEL其实是对方法的一种包装,将方法名包装成一个SEL类型的数据,这个数据其实是函数的地址,去找对应的方法地址。找到方法地址就可以调用方法。其实消息就是SEL。

    //调用方法有两种方式

    // performSelector:(SEL);

    // @selector(test2)返回SEL类型的数据

    Person * p = [[Person allocinit];

    // 间接调用test2方法---------------------------

   SEL s =@selector(test2);

    [p performSelector:s];

    [pperformSelector:@selector(test2)];

    // 直接调用test2方法---------------------------

    [ptest2];

    // 1.test2包装成SEL类型的数据

   // 2.根据SEL数据找到对应的方法地址

   // 2.根据方法地址调用对应的方法

    [ptest3:@"123"];

    // performSelector:(SEL) withObject:(id)

   //此处一定要注意:方法名test3:这个冒号:是函数名的一部分,不能省略

    // 否则报错:-[Person test3]: unrecognized selector sent to instance  SEL可以理解为selector(方法或消息)的简称

    [p performSelector:@selector(test3:)withObject:@"123" ];

//***************************************************************************************   

- (void)test2

{

    // _cmd实际每个方法中都隐含着一个SEL数据_cmd,代表当前方法

    // _cmd == @selector(test2)

    // SEL数据不能直接打印,可以转为字符串再打印,

    // 转换方法: NSStringFromSelector(<#SEL aSelector#>);

   NSString * str =NSStringFromSelector(_cmd);

    NSLog(@"test2----%@", str);

    // 会引发死循环

    //[self performSelector:_cmd];

}