———————————————————————————————————————————

类方法





  ①类方法:

 

     + 开头的方法(定义的过程形式和对象方法一样,只不过 + 开头,这是唯一的区别)

 

  类方法的调用:

 

     [类名   方法名];

 

  ②对象方法:



     - 开头的方法



  对象方法的调用:



    [实例对象名   方法名];

 

  ★ 代码对比学习:

 

    有一个 Dog类,在里面有这么一个对象方法:

 

     -(void)run;  //对象方法

 

     想调用 run方法,

 

     Dog *dog = [Dog new];

     [dog run];

 

 

    在 Dog类 里面还存在另外一个类方法:

     

     +(void)run;  //类方法

     

     想调用 run 方法,



     [Dog run];





★★★ 在上面的描述中,我们可以看到,两个方法的名称都是  run ,但是只是前面的符号不同,一个是  +  ,另一个是  -  ,这样子的写法是可以的,他表示的是不同的方法,我们可以形象的看作两个方法的“性别”差异。对象方法需要创建实例对象进而调用,但是类方法却可以直接通过类名调用。类方法的好处就是不需要给他分配堆内存(因为类方法调用不需要创建空间),节省了内存空间。





———————————————————————————————————————————

类方法使用注意事项



①类方法 可以和 对象方法(实例方法) 同名,并不互相影响使用。

②类方法 可以从 父类 继承而来,子类可以重写类方法。

③类方法 和 对象方法 一样从 @interface…@end 里面声明,在 @implementation…@end 里面实现。

④类方法 只能类名调用,实例对象名调用是不可以的。 对象方法 也是这样,对象方法只能被 实例对象名调用,而不能被类名调用。

⑤在类方法里使用了self,这个self执行的是类对象(class object)而不是实例对象(instance object)



例子:(这里为了方便观察,我将头文件和源文件写在了一起)



#import <Foundation/Foundation.h>



@interface Caculator : NSObject

-(int)add:(int)num1 andNum2:(int)num2;

+(int)add:(int)num1 andNum2:(int)num2;

@end



@implementation Caculator

-(int)add:(int)num1 andNum2:(int)num2

{

    return num1+num2;

}

+(int)add:(int)num1 andNum2:(int)num2

{

    return num1+num2;

}

@end



int main(int argc, const char * argv[]) {

    @autoreleasepool {

        Caculator *caculator=[Caculator new];

        int result1=[caculator add:12 andNum2:13];

        

        int result2=[Caculator add:22 andNum2:15];

        

        NSLog(@"%d,%d",result1,result2);

    }

    return 0;

}





分析:上面是一个Caculator类,然后里面分别写了一个类方法和一个对象方法,显然除了方法名头上标识它们到底是什么方法的符号外,这两个方法是同名的(方法名都是 add: andNum2:),我们用一个实例对象和类名分别调用这两个方法,显然,是正确的,也没有互相影响。



然后我们再在上面的方法声明中添加一个对象方法(上面没有罗列,需要读者自己去写),如:    -(int)jian:(int)num1 andNum2:(int)num2;   这个我们认为是一个减法的对象方法。(实现部分自己写) 然后我们用类名去调用它:      [Caculator jian:14 andNum2:12];

显然这是错误的,系统报错: No known class method for selector ‘jian:andNum2:' 。这句话告诉我们,系统不知道有名为 ‘jian:andNum2:’ 的类方法(class method)。





———————————————————————————————————————————

类方法的调用和要点汇总



这一部分,设计并验证了类方法的调用这个知识点。

首先我们应该清楚,类方法是灵活的,我们设计了两个大的方面去研究类方法的调用。



(1)★对于同一个类而言★

①对象方法的内部可以调用类方法(对—>类)

②类方法的内部可以去调用同类的另外一个类方法(类—>类)

③类方法的内部可以调用对象方法(类—>对)



(2)★对于不同的类而言★(举例两个类:A类和B类)

①A的对象方法的内部可以调用B的类方法(对—>类)

②A的类方法的内部可以调用B的类方法(类—>类)

③A的类方法的内部可以调用B的对象方法(类—>对)



下面为了验证以上的6条,我设计并实现了下面的程序。(这个程序将两个类(Car和Dog)的声明和实现都写在了一起,方便观察)



#import <Foundation/Foundation.h>



@interface Car : NSObject

-(void)c1;

+(void)c2;

+(void)c3;

+(void)test:(Dog *)dog;

@end



@implementation Car

-(void)c1

{

    NSLog(@"这里是Car的对象方法c1。我们会在下面调用Car的其中一个类方法c2。");

    [Car c2];

}

+(void)c2

{

    NSLog(@"这里是Car的类方法c2。我们会在下面调用Dog的类方法d1");

    [Dog d1];

}



+(void)c3

{

    NSLog(@"这里是Car的类方法c3。我们会在下面调用Dog的对象方法d4");

    Dog *dog11=[Dog new];

    [dog11 d4];

}



+(void)test:(Dog *)dog

{

    [dog d2];

    

    Dog *dd=[Dog new];

    [dd d2];

}

@end



@interface Dog : NSObject

+(void)d1;

-(void)d2;

+(void)d3;

-(void)d4;

@end



@implementation Dog

+(void)d1

{

    NSLog(@"这是Dog的类方法d1。我们会在下面调用Dog的类方法d3");

    [Dog d3];

}

-(void)d2

{

    NSLog(@"这是Dog的对象方法d2。我们会在下面调用Car的类方法c3");

    [Car c3];

}

+(void)d3

{

    NSLog(@"这是Dog的类方法d3。我们会在下面调用Dog的对象方法d2");

    Dog *dd1=[Dog new];

    [dd1 d2];

}

-(void)d4

{

    NSLog(@"这里是Dog的对象方法d4。");

}

@end



int main(int argc, const char * argv[]) {

    @autoreleasepool {

        Car *car=[Car new];

        [car c1];   //★★★断点设置处★★★

    }

    return 0;

}





上面的程序读者可以拷贝去验证一下,整个流程是这样的:



调用Car的对象方法(c1)    在方法内      调用Car的类方法(c2)

调用Car的类方法(c2)    在方法内      调用Dog的类方法(d1)

调用Dog的类方法(d1)    在方法内      调用Dog的类方法(d3)

调用Dog的类方法(d3)    在方法内      调用Dog的对象方法(d2)

调用Dog的对象方法(d2)    在方法内      调用Car的类方法(c3)

调用Car的类方法(c3)    在方法内      调用Dog的对象方法(d4)



我们可以将断点设置在main函数中合适位置,然后Step into去一步一步的来看整个程序的流程,观察跳跃的过程。便于理解。





再说一点,因为我们这里学习的是类方法的调用,所以关于“在A的对象方法 内部调用 A的另一个对象方法”(对—>对)和“在A的对象方法 内部调用 B的对象方法”(对—>对)这两个情况我们就不做验证了,当然结果也是一定的,同样可以互相调用。有兴趣的同学可以去验证一下。





★★★另外说明一下这些情况:



★//对象方法中可以调用其他的对象方法

    // (1)在当前对象方法内部创建对象(可以是当前类实例对象,也可以是其他类的实例对象),使用新创建的对象调用对象方法

    // (2)可以使用self

    // (3)对象作为方法的参数传递过来,可以使用传递过来的对象调用方法



★//在类方法中可以调用其他类方法

    //(1)可以直接使用本类类名(或者其他类名)调用类方法

    //(2)可以使用self

(另外,在类方法中不允许访问实例变量(也就是不允许出现类的声明中属性成员),如下图)

_speed是Car类声明的一个属性成员,这是不允许出现在类方法中的。因为调用类方法没有分配存储空间,堆区是空的,所以没法存储值,所以不能访问。



★//在类方法中可以调用对象方法

    //(1)对象作为方法的参数传递过来

    //(2)可以创建一个对象再调用

(如下图)

最后一点需要说明的,那就是类方法不能调用自身,否则就会出现死循环(无法靠自身控制终止循环)。(如下图,在类方法 t1 的内部再次调用了 t1 ,那么一旦在main函数中 [Car t1];   ,那么循环就不会终止)

我还去验证了一下对象方法是否能在内部去调用自身,会出错,这个很无聊,大家不用去验证了~

———————————————————————————————————————————

类方法的一个应用(IPhone 颜色)



#import <Foundation/Foundation.h>

typedef enum {kColorWhite,kColorBlack,kColorTHJ} Color;



@interface IPhone : NSObject

{

    @public

    Color _color;

}



+(NSString *)toGetIPhoneColor:(Color)color;

//返回值类型需要是 字符串类型



@end



@implementation IPhone

+(NSString *)toGetIPhoneColor:(Color)color

{

    NSString *str;//我们需要一个字符串变量来接收返回的值

    //这里用了 switch语句 用来分别判断代码表示的什么颜色,然后转化为汉字字符串返回

    switch (color) {

        case kColorWhite:

            str=@"白色";

            break;

        case kColorBlack:

            str=@"黑色";

            break;

        case kColorTHJ:

            str=@"土豪金";

            break;

        default:

            break;

    }

    return str;

}

@end



int main(int argc, const char * argv[]) {

    @autoreleasepool {

        

        //我们需要显示手机颜色的一个方法,所以这个方法只需要往里面传值就可以了,我们需要把我们定义的枚举类型的值转化为能让人理解的汉字去表示颜色。这个地方不需要创建对象,所以要用类方法。我们调用类方法去接收并处理一个颜色,最后返回一个字符串类型的值,然后用一个字符串变量去接收这个值,然后输出这个值,这就是整个思路

        NSString *str=[IPhone toGetIPhoneColor:kColorTHJ];

        NSLog(@"%@",str);

    }

    return 0;

}





———————————————————————————————————————————

 匿名类的概念及使用



#import <Foundation/Foundation.h>

#import "Car.h"

int main(int argc, const char * argv[]) {

    @autoreleasepool {

        //①使用匿名类可以调用方法

        [[Car new] start];//使用匿名类可以调用方法(其实这里理解起来可能有些别扭,这里为什么叫做匿名类而不是别的。我们可以暂时这样理解:之前我们见到new的时候是创建实例对象的时候,而现在我们申请内存空间了也初始化了,但是却没有实例对象,所以不能叫匿名对象,那么这个东西在类里面出现的,就暂且叫为匿名类吧。new是一个方法)

        

        [[[Car alloc]init]start];//这句话等价于上面的 [[Car new] start];  ,我们知道new做了两个事情,一个是分配存储空间(alloc),另外一个就是初始化(init)。new是alloc和init的结合,平时因为方便,我们一直用new,其实两种写法都一样。

        [[[Car alloc]init]stop];

        

        //②使用匿名类可以访问实例变量(成员变量)

        [Car new]->_speed=100;

        NSLog(@"speed:%d",[Car new]->_speed);

        //输出 speed:0

        //对于用匿名类访问实例变量(成员属性),★只能访问一次★,我们第一次访问设置_speed值为 100 ,但是输出的时候相当于又访问了一次,此时没有设置值,所以就输出系统自动初始化的值,也就是 0

    }

    return 0;

}



//  匿名类是不能有名字的类,它们不能被引用,只能在创建时用New语句来声明它们。匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。

//  ★上面这句话说的很好,总结为:编译时声明,运行时实例化!





———————————————————————————————————————————

new 和 alloc/init 的区别



在上面一小节我们对new有了新的认识,现在我重点去解释一下这两个的相同于不同。



1.在实际开发中很少会用到new,一般创建对象看到的全是[[className alloc] init]

但是并不意味着你不会接触到new,在一些代码中还是会看到[className new],

还有去面试的时候,也很可能被问到这个问题。

2.那么,他们两者之间到底有什么区别呢

我们看源码:



+ new

{

    id newObject = (*_alloc)((Class)self, 0);

    Class metaClass = self->isa;

    if (class_getVersion(metaClass) > 1)

        return [newObject init];

    else

        return newObject;

}



而 alloc/init 像这样:

+ alloc

{

    return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());

}

- init

{

    return self;

}



通过源码中我们发现,[className new]基本等同于[[className alloc] init];

区别只在于alloc分配内存的时候使用了zone.

它(zone)是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的代价,提升了程序处理速度;



3.而为什么不推荐使用new?

不知大家发现了没有:如果使用new的话,初始化方法被固定死只能调用init.

而你想调用initXXX怎么办?不可能!据说最初的设计是完全借鉴Smalltalk语法来的。

传说那个时候已经有allocFromZone:这个方法,

但是这个方法需要传个参数id myCompanion = [[TheClass allocFromZone:[self zone]] init];

这个方法像下面这样:



+ allocFromZone:(void *) z

{

    return (*_zoneAlloc)((Class)self, 0, z);

}



//后来简化为下面这个:

+ alloc

{

    return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());

}



但是,出现个问题:这个方法只是给对象分配了内存,并没有初始化实例变量。

是不是又回到new那样的处理方式:在方法内部隐式调用init方法呢?

后来发现“显示调用总比隐式调用要好”,所以后来就把两个方法分开了。



★★★概括来说,new和alloc/init在功能上几乎是一致的,分配内存并完成初始化。

差别在于,采用new的方式只能采用默认的init方法完成初始化,

采用alloc的方式可以用其他定制的初始化方法★★★





———————————————————————————————————————————

版权声明:本文为博主原创文章,未经博主允许不得转载。

Objective-C 学习笔记(Day 3,上)的更多相关文章

  1. SpringMVC:学习笔记(8)——文件上传

    SpringMVC--文件上传 说明: 文件上传的途径 文件上传主要有两种方式: 1.使用Apache Commons FileUpload元件. 2.利用Servlet3.0及其更高版本的内置支持. ...

  2. Django:学习笔记(8)——文件上传

    Django:学习笔记(8)——文件上传 文件上传前端处理 本模块使用到的前端Ajax库为Axio,其地址为GitHub官网. 关于文件上传 上传文件就是把客户端的文件发送给服务器端. 在常见情况(不 ...

  3. Web安全学习笔记 SQL注入上

    Web安全学习笔记 SQL注入上 繁枝插云欣 --ICML8 SQL注入分类 SQL注入检测 一.注入分类 1.简介 SQL注入是一种代码注入技术用于攻击数据驱动的应用程序在应用程序中,如果没有做恰当 ...

  4. springmvc学习笔记--支持文件上传和阿里云OSS API简介

    前言: Web开发中图片上传的功能很常见, 本篇博客来讲述下springmvc如何实现图片上传的功能. 主要讲述依赖包引入, 配置项, 本地存储和云存储方案(阿里云的OSS服务). 铺垫: 文件上传是 ...

  5. DP动态规划学习笔记——高级篇上

    说了要肝的怎么能咕咕咕呢? 不了解DP或者想从基础开始学习DP的请移步上一篇博客:DP动态规划学习笔记 这一篇博客我们将分为上中下三篇(这样就不用咕咕咕了...),上篇是较难一些树形DP,中篇则是数位 ...

  6. Javaweb学习笔记10—文件上传与下载

    今天来讲javaweb的第10阶段学习.文件的上传与下载,今天主要说的是这个功能的实现,不用说了,听名字就是外行人也知道肯定很重要啦. 老规矩,首先先用一张思维导图来展现今天的博客内容.       ...

  7. 程序设计基础·Java学习笔记·面向对象(上)

    Java程序设计基础之面向对象(上) (自适应学习进度而进行记录的笔记,希望有一些小小的用处吧(^∀^●)ノシ) (新人上路,望多指教,如有错误,望指正,万分感谢(o゚v゚)ノ) 目录 一.面向对象 ...

  8. python学习笔记五 模块上(基础篇)

    模块学习 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要 ...

  9. Solr学习笔记-在Tomcat上部署执行Solr

    上一篇我们初识了Solr而且学习了Jetty的启动方式.查看了Solr的管理界面,这一篇我们来实如今Tomcat上部署执行Solr. 部署环境: window7 jdk1.6.0_14 Solr-4. ...

  10. MongoDB学习笔记五—查询上

    数据准备 { , "goods_name" : "KD876", "createTime" : ISODate("2016-12- ...

随机推荐

  1. [C语言 - 11] 语言编译执行

    使用gcc编译器 1.预编译 gcc -E Hello.c -o Hello.i 2.汇编 gcc -S Hello.i -o Hello.s   3.编译 gcc -c Hello.s -o Hel ...

  2. 转载LINQ TO Entity 在数据库发生更改时更新实体数据模型 .edmx 文件

    转载原出处:http://blog.csdn.net/litao2/article/details/8629335 在“模型浏览器”中,右击 .edmx 文件,然后选择“从数据库更新模型”. 模型更新 ...

  3. UI进阶 数据处理之文件读写

    目录: 1-------沙盒机制(SandBox) 2-------简单对象的读写(I/O)操作 3-------复杂对象的读写(I/O)操作 一.沙盒机制(SandBox) 什么是沙盒:每个iOS应 ...

  4. jquery基础篇

    1.jquery选择器和css选择器的关系: jquery的选择器是源于css,jquery支持css1和css2的全部和css3 的部分选择器,同时它也有少量独有的选择器. 2.常用jquery选择 ...

  5. ios键盘上添加辅助视图

  6. Oracle- 日期格式和数字类型处理

    更新数据库时间格式的显示格式的语句:(alter session set nls_date_format='YYYY-MM-dd'); to_date("要转换的字符串",&quo ...

  7. 从零开始学android开发-IDE空间不够报错

    E:\ProSoft\adt-bundle-windows-x86-20140321\eclipse目录下 右键eclipse用记事本打开 可以设置运行的最大的运行空间

  8. 继续推广我的新博客xysay:http://www.xysay.com/

    RT 博客收拾了一下,准备以后就在那里记录论文笔记啦,求交流,求推荐,求友链~~~ http://www.xysay.com/

  9. CustomViewWith_Image_Text_Video

    CustomViewOfTextVideoImage.rar https://github.com/Grishu/CustomViewWith_Image_Text_Video

  10. [AngularJS] 'require' prop in Directive or Component

    When use 'require', recommend to add some error check, for example: class ChildCtrl { constructor(){ ...