IOS可以通过Categories类别和Extensions拓展在你不知道一个类的源码情况下,向这个类添加扩展的方法、成员变量、将类的实现分散到多个不同文件或多个不同框架中。协议protocol和代理Delegate一套则是在自己的类中得到其他类的通知。
创建.m文件的时候,可以选择这三个类型,这里说下这三个类型的使用
一、类别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
如果没有拓展修改属性的话,会报错的
某些情况下,我们需要声明一个@property,它对外是只读的(readonly),而对内是可读写的(readwrite),这时,可以通过Extensions实现。
三、协议protocol、代理Delegate
如果只是用协议,那几乎没什么用,就是创建一个需要实现的协议而已,配合代理Delegate使用才更有意义
@protocol ViewControllerTestDelegate NSObject
@required
-(void)showTime:(NSString*)myTime;
@optional
-(void)showName:(NSString*)myName;
@end
协议protocol中,required关键词是继承协议的类必须要写的,optional则是可选的。
代理的作用就是类A声明为类B的代理,当类B做出相关动作的时候,类A会响应到类B的代理函数。
这里以两个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/HudongBlogDemo/tree/master/CategoryAndExtensionTest
Gitosc下载:http://git.oschina.net/DamonHoo/CategoryAndExtensionTest
五、参考文章
- iOS开发-Protocol协议及委托代理(Delegate)传值
- iOS开发之protocol和delegate
- 让Category支持添加属性与成员变量
- @dynamic关键字的作用
- Objective-C中的@dynamic
- iOS 类别和扩展(Categories和Extensions)
- iOS - 类扩展与分类的区别
- ios 分类(Category)
- iOS类别(Category)与扩展(Extension)
版权属于:东哥笔记 - DongGe.org
本文链接:https://dongge.org/blog/396.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!