Objective-C中的实例方法、类方法、Category、Protocol

1.方法

Objective-C中的方法有两种:

1.1 实例方法

-开头的方法是实例方法。它属于类的某一个或某几个实例对象,即类对象必须实例化后才可以使用的方法,将消息发送给实例对象:

// Deck.h

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject

@property(nonatomic) int cardNum;

// 实例方法
- (Card *)randomDrawCard;

+ (NSString *)CardKinds;

@end

实例方法中可以使用该类的所有实例变量:

// Deck.m

#import "Card.h"

@implementation Deck

- (Card *)drawCardFromTop
{
    // 实例变量
    _cardNum--;

    //TODO.....
}

+ (NSString *)CardKinds
{
    NSLog("Cards are divided into four kinds: spades, diamonds, clubs and hearts");
}

@end 

1.2 类方法

+开头的方法是类方法。Objc中的类方法类似Java中的static静态方法,它是属于类本身的方法,不属于类的某一个实例对象,所以不需要实例化类,用类名即可使用,是将消息发送给类:

// Deck.h

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject

- (Card *)randomDrawCard;

// 类方法
+ (NSString *)CardKinds;

@end    

类方法不能使用任何实例变量:

// Deck.m

#import "Card.h"

@implementation Deck

- (Card *)drawCardFromTop
{
    // 实例变量
    _cardNum--;

    //TODO.....
}

// 不能使用该类的实例变量_carNum
+ (NSString *)CardKinds
{
    NSLog("Cards are divided into four kinds: spades, diamonds, clubs and hearts");
}

@end 

所以我们使用类方法一般有两种情况:

  • 创建一些事物,比如特殊格式的字符串等。
  • 作为工具方法,比如返回常数等。

1.3 类方法和实例方法认知的误区

  • 类方法常驻内存,所以比实例方法效率高。
    事实上,在加载时机和占用内存上,类方法和实例方法是一样的,在类第一次被使用时加载方法,所以在效率上没有什么区别。

  • 类方法分配在堆上,实例方法分配在栈上。
    事实上,所有的方法都不可能分配在堆栈区,方法作为二进制代码是存储在内存的程序代码区,这个内存区域是不可写的。请查看我这篇笔记中的相关概念Objective-C中的Block

1.4 总结

实例方法和类方法有大多数的共性,比如都可以有一个或多个参数、都可以继承基类的方法、相同的声明规范等。唯一不同的就是类方法不能使用实例变量,所以导致它只适用于一些特殊的情况。

2.Category

如果我们想给一个已存在的、很复杂的类添加一个新的方法,应该怎么做?
想翻源码添加?骚年,你太天真,你如果看不到源码呢。即便我们可以看到源码,如果我们新增的逻辑也很复杂,这样就会扩大原始设计的规模,有可能会打乱整个设计的结构。
Category就是Objective-C提供的为我们解决这一问题的方法。它可以让我们动态的在已经存在的类中添加新的行为,即方法。对类进行扩展时不需要访问其源码,也不需要创建子类。

2.1 使用方法

Category的实现很简单,我们举例说明。

// Deck.h

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject

- (Card *)randomDrawCard;

@end

这是类Deck的声明文件,其中包含一个实例方法randomDrawCard,如果我们想在不修改原始类、不增加子类的情况下,为该类增加一个drawCardFromTop方法,只需要定义两个文件Deck+DrawCardFromTop.hDeck+DrawCardFromTop.m,在声明文件和实现文件中用()把Category的名称括起来即可,声明文件如下:

// Deck+DrawCardFromTop.h

#import "Deck.h"
#import "Card.h"

@interface Deck(DrawCardFromTop)

- (Card *)drawCardFromTop;

@end

实现文件如下:

// Deck+DrawCardFromTop.m

#import "Deck+DrawCardFromTop.h"
#import "Card.h"

@implementation Deck(DrawCardFromTop)

- (Card *)drawCardFromTop
{
    //TODO.....
}

@end

DrawCardFromTop是Category的名称。这里一般使用约定俗成的习惯,将声明文件和实现文件统一采用"原类名+Category名"的方式命名。
使用也非常简单,引入Category的声明文件,然后正常调用即可:

// main.m

#import "Deck+DrawCardFromTop.h"
#import "Card.h"

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

    Deck *deck = [[Deck alloc] init];
    Card *card = [deck drawCardFromTop];

    return 0;

}

2.2 使用场景

  • 需求变更在整个开发周期是司空见惯的事情,那么我们可能就需要对某个或某几个类中添加新的方法以满足需求。
  • 我们在团队协作开发时候,经常需要多个人来实现一个类中的不同方法,在这种情况下采用Category是一个较好的选择。
  • 当一些基础类库满足不了我们的需求时,我们希望能扩展基础类库,这时就需要Category。

2.3 需要注意的问题

  • Category可以访问原始类的实例变量,但不能添加变量,如果想添加变量,可以考虑通过继承创建子类。
  • Category可以重载原始类的方法,但不推荐这么做,这么做的后果是你再也不能访问原来的方法。如果确实要重载,正确的选择是创建子类。
  • 和普通接口有所区别的是,在分类的实现文件中可以不必实现所有声明的方法,只要你不去调用它。

2.4 总结

掌握并用好Category可以充分利用Objective-C的动态特性,编写出灵活简洁的代码。

3.Protocol

简单来说,Protocol不属于任何一个类,它只是一个方法列表,任何类都可以对其中声明的方法进行实现。这种设计模式一般称为代理模式(delegation)。你可以通过Protocol定义各种行为,在不同的场景采用不同的实现方式。在iOS和OS X开发中,Apple采用了大量的代理模式来实现MVC中View和Controller的解耦。

3.1 使用方法

Protocol有两种声明的方式:

  • 在单独的声明文件(.h文件)中声明。
  • 在某个类的声明的文件中声明。

以上两种方式视具体情况而定,但是在代码规范上都是一致的:

// HandleDeckDelegate.h

@protocol HandleDeckDelegate <NSObject>

@required
- (void)ShuffleDeck;

@optional
- (void)CuttingDeck;

@end

上述代码中有两个关键字,@required@optional,表示如果要实现这个协议,那么ShuffleDeck方法是必须要实现的,CuttingDeck则是可选的,如果不注明,那么方法默认是@required的,必须实现。

那么如何实现这个Protocol呢,很简单,创建一个普通的Objective-C类,如果Protocol使用单独的.h文件声明,那么在该类的.h声明文件中引入包含Protocol的.h文件,如果Protocol是声明在一个相关类中,那么就需要引入该类的.h文件。之后声明采用这个Protocol即可:

// Deck.h

#import <Foundation/Foundation.h>
#import "Card.h"
#import "HandleDeckDelegate.h"

@interface Deck : NSObject<HandleDeckDelegate>

- (Card *)randomDrawCard;

@end

用尖括号(<...>)括起来的HandleDeckDelegate就是我们创建的Protocol。如果要采用多个Protocol,可以在尖括号内引入多个Protocol名称,并用逗号隔开即可。例如<HandleDeckDelegate,xxxDelegate>

// Deck.m

#import "Card.h"

@implementation Deck

- (Card *)drawCardFromTop
{
    //TODO.....
}

- (void)ShuffleDeck
{
    //TODO.....
}

@end

由于CuttingDeck方法是可选的,所以我们只实现了ShuffleDeck

3.2 使用场景

  • Objective-C里的Protocol和Java语言中的接口很类似,如果一些类之间没有继承关系,但是又具备某些相同的行为,则可以使用Protocol来描述它们的关系。
  • 不同的类,可以遵守同一个Protocol,在不同的场景下注入不同的实例,实现不同的功能。

3.3 需要注意的问题

  • 根据约定,框架中后缀为Delegate的都是Protocol,例如UIApplicationDelegateUIWebViewDelegate等。
  • Protocol本身是可以继承的,比如:
@protocol A
    -(void)methodA;
@end

@protocol B <A>
    -(void)methodB;
@end

如果你要实现B,那么methodA和methodB都需要实现。

  • Protocol是与任何类都无关的,任何类都可以实现定义好的Protocol,如果我们想知道某个类是否实现了某个Protocol,那么我们可以用conformsToProtocol方法进行判断:
[obj conformsToProtocol:@protocol(ProcessDataDelegate)] 

3.4 总结

Protocol最常用的就是委托代理模式,Cocoa框架中大量采用了这种模式实现数据和UI的分离。例如UIView产生的所有事件,都是通过委托的方式交给Controller完成。

本文首发地址:Objective-C中的实例方法、类方法、Category、Protocol

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容