IOS使用类别Categories、拓展Extensions、协议protocol、代理Delegate

IOS可以通过Categories类别和Extensions拓展在你不知道一个类的源码情况下,向这个类添加扩展的方法、成员变量、将类的实现分散到多个不同文件或多个不同框架中。协议protocol和代理Delegate一套则是在自己的类中得到其他类的通知。

创建.m文件的时候,可以选择这三个类型,这里说下这三个类型的使用

屏幕快照 2017-01-12 22.12.20.jpg

一、类别Categories

我们可以不用继承系统类,直接给系统类添加方法,最大程度的体现了Objective-C的动态语言特性,

(1) Category的方法不一定非要在@implementation中实现,也可以在其他位置实现,但是当调用Category的方法时,依据继承树没有找到该方法的实现,程序则会崩溃。

(2) Category理论上不能添加变量,但是可以使用@dynamic 来弥补这种不足。 (即运行时Runtime)

1.1、类别添加函数

先说最简单的,创建一个ViewController类别文件,给ViewController增加一个输出函数

ViewController+ViewControllerCategory.h文件

#import "ViewController.h"

@interface ViewController (ViewControllerCategory)

-(void)testCategoty;
@end

ViewController+ViewControllerCategory.m文件

#import "ViewController+ViewControllerCategory.h"

static const void *strKey = &strKey;

@implementation ViewController (ViewControllerCategory)

-(void)testCategoty
{
    [self testLog];
}

在ViewController+ViewControllerCategory.m文件中,self相当于ViewController,在ViewController.h里面声明过的函数,都可以直接在这里面使用,比如这里的[self testLog];就是ViewController声明的函数。

1.1.1、函数的使用

ViewController.h文件

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

//输出
-(void)testLog;
@end

ViewController.m文件

#import "ViewController.h"

import "ViewController+ViewControllerCategory.h"

@interface ViewController ()
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //测试类别Categoty
    [self testCategoty];
}

-(void)testLog
{
    NSLog(@"testLog");
}
@end

在这里ViewController可以直接使用类别里面的testCategoty函数了。

1.2、类别添加变量

Category理论上不能添加成员变量,虽然不推荐,如果添加变量可以使用拓展Extensions添加,但是如果非要想在类别中添加变量,可以通过dynamic来添加。

它与@synthesize的区别在于:使用@synthesize,编译器会确实的产生getter和setter方法,而@dynamic仅仅是告诉编译器这两个方法在运行期会有的,无需产生警告。

通过runtime.h中objc_getAssociatedObject / objc_setAssociatedObject来访问和生成关联对象。通过这种方法来模拟生成属性。

ViewController+ViewControllerCategory.h文件

#import "ViewController.h"

@interface ViewController (ViewControllerCategory)
@property (strong,nonatomic) NSString *str;

@end

ViewController+ViewControllerCategory.m文件

#import "ViewController+ViewControllerCategory.h"

import <objc/runtime.h>

static const void *strKey = &strKey;

@implementation ViewController (ViewControllerCategory)
@dynamic str;

- (id)str
{
    return objc_getAssociatedObject(self, strKey);
}

-(void)setStr:(id)strs
{
     objc_setAssociatedObject(self, strKey, strs, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

1.2.1、成员变量的使用

ViewController.h文件

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

ViewController.m文件

#import "ViewController.h"

import "ViewController+ViewControllerCategory.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setStr:@"mm"];
    [self testLog];
}

-(void)testLog
{
    NSLog(@"%@",self.str);
}
@end

这里就可以输出设置的值mm,并且str直接当成员变量使用即可

二、拓展Extensions

拓展可以看成匿名的类别,extensions没有.m文件,最大的作用就是对成员变量的作用。

2.1、创建拓展

ViewController_ViewControllerExtension.h文件

#import "ViewController.h"

@interface ViewController ()

@property (retain, readwrite) NSString* blog;

-(void)changeBlog;

@end

拓展和类别的区别就是拓展是匿名的,括号里面是没有内容的。没有.m文件,所以这个函数在ViewController.m的implementation中实现即可。

2.2、拓展对成员变量的使用

ViewController.h文件

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
@property(strong,readonly) NSString *blog;

//输出
-(void)testLog;
@end

注意在ViewController.h文件中,以前的成员变量的blog属性是readonly的,只读的

ViewController_ViewControllerExtension.h文件

#import "ViewController.h"

@interface ViewController ()

@property (retain, readwrite) NSString* blog;

-(void)changeBlog;

@end

在这个拓展里面,则把成员变量blog属性更改为readwrite,可读写的,这样就更改了这个成员变量

ViewController.m文件

#import "ViewController.h"

import "ViewController_ViewControllerExtension.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self changeBlog];
    //测试类别Categoty
    [self testCategoty];
}

-(void)testLog
{
    NSLog(@"%@",self.blog);
}

-(void)changeBlog
{
    self.blog = @"hudong";
}

@end

如果没有拓展修改属性的话,会报错的

屏幕快照 2017-01-12 18.48.24.png

某些情况下,我们需要声明一个@property,它对外是只读的(readonly),而对内是可读写的(readwrite),这时,可以通过Extensions实现。

三、协议protocol、代理Delegate

如果只是用协议,那几乎没什么用,就是创建一个需要实现的协议而已,配合代理Delegate使用才更有意义

@protocol ViewControllerTestDelegate <NSObject>

@required
-(void)showTime:(NSString*)myTime;

@optional
-(void)showName:(NSString*)myName;

@end

协议protocol中,required关键词是继承协议的类必须要写的,optional则是可选的。

屏幕快照 2017-01-12 17.59.11.png

代理的作用就是类A声明为类B的代理,当类B做出相关动作的时候,类A会响应到类B的代理函数。<br/>

这里以两个ViewController为例,当ViewController修改SecondViewController的Name和Time的时候,ViewController会输出SecondViewController修改过的Name和Time。

3.1、SecondViewController中的代理方法

SecondViewController.h文件

#import <Foundation/Foundation.h>

import <UIKit/UIKit.h>

@protocol ViewControllerTestDelegate <NSObject>

@required
-(void)showTime:(NSString*)myTime;

@optional
-(void)showName:(NSString*)myName;
@end

@interface SecondViewController : UIViewController
@property(strong,nonatomic) id<ViewControllerTestDelegate> delegate;

-(void)changeName;
-(void)changeTime;
@end

SecondViewController.m文件

#import "SecondViewController.h"

@interface SecondViewController ()
@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

-(void)changeTime
{
    [self.delegate showTime:@"2016-01-12"];
}

-(void)changeName
{
    [self.delegate showName:@"dongdongBlog"];
}

@end

3.2、ViewController作为代理

ViewController.h文件

#import <UIKit/UIKit.h>

import "SecondViewController.h"

@interface ViewController : UIViewController<ViewControllerTestDelegate>

@end

ViewController.m文件

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    SecondViewController *view = [[SecondViewController alloc] init];
    view.delegate = self;
    [view changeName];
    [view changeTime];
}

pragma mark --代理里面的函数

-(void)showTime:(NSString*)myTime
{
    NSLog(@"%@",myTime);
}

-(void)showName:(NSString*)myName
{
    NSLog(@"%@",myName);
}

@end

这样会自动输出time和name了。

四、Demo下载:

Github下载:https://github.com/DamonHu/CategoryAndExtensionTest

Gitosc下载:http://git.oschina.net/DamonHoo/CategoryAndExtensionTest

五、参考文章

Last modification:February 13th, 2017 at 10:43 pm
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment