iOS成长之路基础篇 – (3) OC 类的细节

iOS成长之路基础篇 – (3) OC 类的细节

前面的两篇已经算是把基本的语法规则总结完了,我准备在这篇里面总结一下OC关于类的一些细节信息,比如id关键字,类的加载,初始化等。写到这里,我想起了前几天微博上看到的有一位同学总结的iOS学习路线图,在这里我给出需要的同学链接:iOS学习路线图(曾宪华)。我这几天总结的几篇博客,其实只是iOS路线图上的一个点。希望大家能根据自己的情况把这个路线图上自己感兴趣的方向的技能都学习一遍。

id类型

在Objective-C 中,id关键字是一种特殊类型,我们称之为id类型,同时也叫做万能的指针,它就相当于[NSObject *]。这种类型被定义为指向对象的指针,在定义变量的过程中,id已经包含了*,所以后面不需要加 *号。同时它只能用于OC对象。

id在OC中的定义是下面的这种类型:

typedef struct objc_object {

    Class isa;

} *id; 

从上面看出,id 是指向struct objc_object 的一个指针。也就是说,id 是一个指向任何一个继承了Object(或者NSObject)类的对象。在后面的代理设计模式中我们会用到它。

OC类的实例创建

  • new方法

在Objective-C中,完整的创建一个可用的对象需要两步:

  1. 分配存储空间:+alloc
  2. 初始化:-init
Person *p = [Person alloc];    // 分配存储空间
Person *p = [p init];          // 初始化

//[[Person alloc] init]        //可以将上面两步和起来写

以上两句也可以连着写在一起,如同最后那一句,同时还等价于[Person new]。

  • 构造方法init

alloc方法是系统实现的,涉及到一些OC语言底层的信息,一般我们不能重写+alloc方法,但是在实际开发过程中,我们可能会经常重写-init方法,在这个方法将常会对我们自己定义的一些成员变量进行初始化。

重写-init方法:

-(instancetype)init
{
    //1、一定要调用super的init方法。初始化父类中的成员变量
    self = [super init];

    //2、如果对象初始化成功,才有必要进行接下来的初始化
    if(self != nil){
        _age = 10;
    }

    //3、一定要返回一个初始化完毕的self
    return self;
}

我们在开发过程中,可能有时我们为了快速创建一个对象,我们就要自定义构造方法,在构造方法中进行一些成员变量的初始化工作。

自定义构造方法规范:

一定是对象方法,一定以“-”开头;

返回值一般为instancetype类型(原来的以id开头);

方法名以init开头。

例如:-(instancetype)initWithName:(NSString *)name andAge:(int)age;

Category(分类)

Category分类是一种不更改原来的类文件,在原来的类中增加方法的方法。

这种机制是利用OC动态运行时分配机制,可以为现有的类添加新的方法。它可以为任何类添加新的方法,包括那些没有源代码的类,还有那些系统API中的类。

在Xcode 7中创建类别是File-New-File…-Objective-C File,File Type对应的选择Category,File对应的是分类名称,class对应的是原类的名称。

生成的文件名称格式:原来的类+分类名.h 、原来的类+分类名.m

分类的声明和实现:

//声明
@interface 类名(分类名称)

//方法声明

@end


//实现
@implementation 类名

//方法实现

@end

注意事项:

分类只能扩充方法,不能扩充成员变量。

分类可以访问原类中的成员变量。

分类的优先级较高。分类中的方法将覆盖原来类中同名的方法。不建议这样做。

多个分类中的同名方法覆盖情况与编译顺序有关。最后编译的覆盖前面编译的。

Class(类)

在Objective-C中,类是一个对象,是Class类型的对象,简称类对象。

Class类型的定义:

// 一个任意的类型,表示一个Objective-C类
typedef struct objc_class *Class;

只加载一次,类名就代表着类对象,每个类只有一个类对象。

Class类型(包含 * )。isa指向类的地址,类方法存储在类对象地址中。

  • 类的加载(顺序加载):它有两个非常特殊的类方法 load 和 initilize ,用于类的加载和初始化。
  1. +(void)load方法:当程序一启动,加载类的时候,调用项目中每一个类的这个方法一次。先加载父类,再加载子类。分类中的load方法最后调用。
  2. +(void)initialize方法:第一次使用这个类的的时候加载。先加载父类,再加载子类。没用到子类的时候,不调用子类的这个方法。分类Category中的方法将覆盖原类中的initialize方法,最后只调用分类中的方法。
  • description方法
+(NSString *)description;    //方法声明在NSObject类中默认输出:类名
-(NSString *)description;    //默认情况下打印<类名:内存地址>

NSLog(@"%@", p);        //<类名:存储的内存地址>
NSLog(@"%p", p);        //存储的内存地址
NSLog(@"%p", &p);       //指针自己的内存地址

注意:

死循环陷阱:不能在重写description方法中调用:NSLog(@”%@”,self);

NSLog输出C语言字符串的时候不能有中文字符,否则不能输出

SEL

在OC中,SEL就是对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法。

SEL的定义如下:

typedef struct objc_selecter *SEL;

作用:

  1. 把方法包装成SEL类型的数据
  2. 根据SEL数据找到方法对应的方法地址
  3. 根据方法地址调用对应的方法

SEL类型数据

每个方法都把消息接收对象称作 self ,而自身的选择器称作 _cmd,其中self为调用者本身,可以是类,可以是对象,上一节中已经总结过,_cmd为自身选择器,即将自身包装成SEL类型的数据。

总结

在这一篇中,我总结了关于类的一些细节信息,通过总结这些内容,对OC语法进行一个系统的复习,主要是因为自己现在也要找工作了,一直以来都是以Swift开发为主,OC能看懂,但是没有开发过,所以为自己接下来找工作储备一些理论基础。下一篇将总结OC中的内存管理方面的知识。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注