转载请注明出处,谢谢。http://blog.youkuaiyun.com/cloosen/article/category/1191708
该文章会不定时更新,因为毕竟一次把他写全有点难,有的东西忘了写了什么的,如果想起来会补充进去,如果大家有什么疑问可以留言,如果我了解会不定时回答的,3Q.
题外话:今天终于转正了,签了一份3年有违约金的合同,之前公司VP面我得时候说过,公司招过无数牛人,并且公司会给每个人充裕的时间学习,有些人学习了,有些人荒芜了,三年后有些人变成了行业巨牛,有些人泯然众人了。觉得自己真该努力了,要不然三年之后就不知道什么样子了。
言归正传,也说说Catagory这个东西,这次学聪明了,不像上次写属性的时候写完了才发现苹果的文档,还好有很多东西跟他不一样,要不然不就白写了?有兴趣的童鞋可以先看看苹果关于类别的文档:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-SW2。当然了如果你觉得英文看着不爽,你可以去搜搜,有人把他翻译成了汉语。
类别(Category),是objective-C所特有的一种设计模式,它用来对现有的类进行方法和属性的扩充和优化,即使在完全不知道现有类的代码的情况下。
最正常的类别写法如下。这个各个blog,各种文档,各种书几乎都是这么写的,因为苹果文档就是这么写的,哈哈。
.h文件
#import <UIKit/UIKit.h>
@interface UIButton (Test)
- (void)myMethod;
@end
.m文件
#import "UIButton+Test.h"
@implementation UIButton (Test)
- (void)myMethod
{
NSLog(@"myMethod is success~~~");
}
@end
其他类
[button myMethod];
这么写没啥好说的,规定的,你用UIButton的实例,他已经包含了该方法。
类别和继承十分的相似,都是对其他类进行扩展,但是他们有两点不同,1是类别中无法加入实例变量,2是类别中不到万不得已千万别重写主类的方法。
先说说第一点类别中无法加入实例变量,但是你可以加入属性。因为没有实例变量,所以你也无法用@synthesize,因为类无法给你自动生成set和get方法,所以我自己写了set和get方法,之前写了个小demo,代码如下:
.h文件
#import <UIKit/UIKit.h>
@interface UIButton (Test)
{
//什么也不能加,不信你试试
}
@property(nonatomic,retain) NSString *myTitle;
- (void)myMethod;
@end
.m文件
#import "UIButton+Test.h"
@implementation UIButton (Test)
//不能写@synthesize
- (NSString *)myTitle
{
return @"hahahaha,you can get~~";
}
- (void)setMyTitle:(NSString *)inputTitle
{
NSLog(@"you want set title :%@",inputTitle);
}
- (void)myMethod
{
NSLog(@"myMethod is success~~~");
}
@end
其他类
button.myTitle=@"have a look";
NSString *abab = button.myTitle;
NSLog(@"%@",abab);
[button myMethod];
可以看出控制台输出结果
可以看到,我们写的set,get方法居然被调用到了,所以我觉得自己实现属性的功能完全靠谱,但是因为没有实例变量,无法存储,所以写得set,get并没起到什么大作用,因为实例变量的最终作用有一点就是帮助你完成set和get方法。实例变量的作用就是保存一些我们想要保存的值,用于过度,所以我们很容易就想出可以用static,搞一个全局变量来替代实例变量,从辅助我们完成方法的编写,这样我们就用了联合存储模式(以后的blog会讲)模拟了添加实例变量的效果,代码如下:
.m文件
#import "UIButton+Test.h"
#import <objc/runtime.h>
static char MYTITLE;
@implementation UIAlertView (Block)
@dynamic myTitle;
- (void)setMyTitle:(NSString *)myTitle
{
objc_setAssociatedObject(self, &MYTITLE, myTitle, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)myTitle
{
return objc_getAssociatedObject(self, &MYTITLE);
}
其中方法可以参考苹果的文档:http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html
提示一下,这个函数API在ios3.1以上才有的,不是说这是联合存储模式实现的么,其实底层用的是NSMapXXX实现的,在ios3.1的时候给封装成了API而已,底层实现将在讲联合存储的时候详细讨论.
然后再说下第2点类别中不到万不得已千万别重写主类的方法
override这个词大家都再熟悉不过,oop中子类对父类的方法进行重写,子类的该方法也能用,父类的该方法也能用,体现了多态特性,类别中也可以重写主类的方法,但是此重写非彼重写,我想用replace比较合适,就是将主类的该方法完全替换掉了,你再也获得不到主类的此方法,一般多此继承的类第一句都会是[super XXXXX],没错,你无法调用主类的父类做的事情了,可以说你少做了很多事情,经常会出现一些很诡异的bug。所以说万不得已千万别重写主类的方法,但是什么时候要重写呢,就是当主类该方法有问题的时候,这样我们重写他,将他原来的有问题代码replace掉,当时调试UIPageViewController的时候有一个苹果内部相关的什么类来的忘了,控制台说XX方法有问题,然后被我重写了之后好了.
什么时候用类别,什么时候用继承,确实是一个很让人纠结的事情,没有必须用类别的情况,也没有必须用继承的情况,按书上说的,如果需要把新的实例变量添加到类中,那么继承是优先选择.类别可以用联合存储的模式来模拟实例变量,但是需要以性能为代价;对复杂的类,进行子类化十分困难,类别可以用来替代继承,一般向基类中添加类别比较好,其所有的子类都能用.
你可以对一个类进行无限次扩展,即写无数个类别,但是有一点是要保证得,你的类别的名字不能重复
类别还有个好处就是可以分布在多个文件中,这样无论在开发还是维护中都非常好。类别之前还被用作创建非正式协议,后来被protocol的@optional给替代了,现在几乎没有人用类别来创建非正式协议了。再后来就有了类的扩展(Class Extensions),它主要用来:1.做函数的前置声明,2.用于定义私有属性,可参见之前的blog:http://blog.youkuaiyun.com/cloosen/article/details/7796398。当Extensions中写了没有被实现的方法时,编译器会报warning,而Category中写了没有被实现的方法时,编译器会当成非正式协议,从而什么都不会做.
最后说一下,其实类别也可以实现protocol,这个我用的比较少,以后搞明白了在加上吧.
协议可以放在类别的.h文件中
@interface UIButton (Test)<UIAlertViewDelegate>
也可以放在类别的.m文件中用Extensions隐藏起来.
以上就是我所了解和掌握的一些关于类别的知识,现在无论是重构还是构建library,用继承还是很多的,因为对类别了解不够深,估计以后在工作中会积累更多了,书上说类别还是要优于继承的,这篇文章也会随着我慢慢了解而不断更新的.
转载请注明出处,谢谢.http://blog.youkuaiyun.com/cloosen/article/category/1191708
------------------------by 郭大虎