为什么使用类目
是一种为现有的类添加新方法的方式
有时需要对现有的类添加一些方法,之前都是通过继承相关的类,然后再子类中扩展我们需要的相应的方法.
Objective-C是一种动态语言,可以给现有的类添加新的方法,这便是对类目的延展.
类目(category):为已知的类添加新的方法,无论是否知道类的源代码,这些类包括自己定义的类和系统已有的类。
类目的作用主要有三个方面:
1、将类的实现分散到多个不同的文件中,将多个类目的声明放入同一个.h文件中,将实现放入多个.m方法中。
2、使用类目创建对私有方法的前向引用,就是为类创建私有方法。
3、向对象添加非正式协议,现在逐渐被正式协议做替代。
类目的实现
类目声明之后需要对类扩展出来的方法进行实现
使用扩展后的方法
#import <Foundation/Foundation.h>
#import "NSString+MyNumber.h"//添加类目头文件
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//创建四个字符串
NSString *str1=@"gdsfgsdfgfdgdfs";
NSString *str2=@"fgsagsdfgfdsg";
NSString *str3=@"打开分为飞大厦俄方飞";
NSString *str4=@"jfghjfghjfghj";
//求出每一个字符串的长度
// int l1=(int)[str1 length];
// int l2=(int)[str2 length];
// int l3=(int)[str3 length];
// int l4=(int)[str4 length];
//
// //转为对象
// NSNumber *m1=[NSNumber numberWithInt:l1];
// NSNumber *m2=[NSNumber numberWithInt:l2];
// NSNumber *m3=[NSNumber numberWithInt:l3];
// NSNumber *m4=[NSNumber numberWithInt:l4];
//创建一个可变的字典
NSMutableDictionary *mDic=[NSMutableDictionary new];
//添加对象到字典中
// [mDic setObject:m1 forKey:str1];
// [mDic setObject:m2 forKey:str2];
// [mDic setObject:m3 forKey:str3];
// [mDic setObject:m4 forKey:str4];
//使用类目后修改为
[mDic setObject:[str1 getStrLength] forKey:str1];
[mDic setObject:[str2 getStrLength] forKey:str2];
[mDic setObject:[str3 getStrLength] forKey:str3];
[mDic setObject:[str4 getStrLength] forKey:str4];
//输出
NSLog(@"%@",mDic);
}
return 0;
}
注意类目里面只能写方法,不能写声明和属性,所以,类目不能作为接口来用
类目的局限性
-类目无法向已有类中添加实例变量
-如果类目中的方法和已有类中的方法名称冲突时,类目中的方法优先级高,如果方法名冲突,已有类中的原始方法便无法使用
-在使用类目的时候,最好是将自己扩展的方法和原始方法区分开来
类目的作用
有三个方面:
1.将类的实现分散到多个不同的文件中
-将多个类目的声明放入同一个.h文件中
-将实现放入多个.m方法中
2.使用类目创建对私有方法的前向引用
-就是为类创建私有方法
3.向对象添加非正式协议
-现在逐渐被正式协议做替代
利用类目分散实现类
-类的@implementation部分只能写在一个.m文件中
-对于大型的类,可以使用类目将类的实现间接的放入多个.m文件中
-将多个类目的声明放入同一个.h文件中,包括多个类目声明的@interface和@end
类私有的方法
-Objective-C中没有绝对意义上的私有方法
-在.h文件中声明的方法都属于公开的方法,意味着开放给别人调用
-如果不想公开某些方法,可以不在.h文件中声明
-这样的方法可以被本类中的其他方法所调用
-如果在类外面强行调用这些未公开的方法,也能调用,但是会有编译器警告
类的延展
延展(extension):
在自己类的实现文件中添加类目(匿名类目)来声明私有方法.(只有本类的.m文件中才能使用)
类的延展可以让类方法成为类的私有方法
将私有方法声明到一个匿名类目中,并且将这个匿名类目放到类的.m文件里
然后在类的.m的@implementation部分将这个匿名类目中的私有方法进行实现
这样的也能够写出私有方法,但是同样也可以强行在类外部调用,也会有编译器警告
使用类目对私有方法反向引用
-如果私有方法的声明放入一个匿名类目中,并且这个类目放入一个.h文件中那么这个.h文件可以看做私有的API
-私有API可以不公开,只开放给许可者来调用
然后在使用私有API时将那个.h文件导入,然后调用私有方法,编译器警告便会消失
respondsToSelector
用来检查某一个对象能否调用某一个方法(检查方法时候有实现)
创建了NSObject的类目之后,类目中声明方法对于NSObject的子类来说如何检查是否实现了哪个方法
因为可能要调用某个NSObject类目中声明的方法,在调用时需要判断是否能够响应才行,否则会造成程序崩溃
MyClass *mc= [MyClass new];
if([mc respondsToSelector:@selector(test)])
{
[mc test];
}
else
{
NSLog(@"未实现test方法!");
}
Objective-C中的协议
协议是一个方法列表,采用协议时需要在声明类时说明要采用的协议
协议的声明
协议就是一种标准,用来定义了实现什么,不关心具体怎么实现
OC的协议是由@protocol声明的一组方法列表
要求其它的类去实现,相当于@interface部分的声明
@required标注的方法为必须实现方法(默认不写为必须实现)
@optional标注的方法为可以选择实现
@protocol 协议名称
//方法列表
@end
协议的实现
方式:
接口文件 一个类可以通过协议,用来遵循多个类中的方法,这样可以做到多继承的效果
@interface ClassName : 父类名 <协议1,协议2…>
@end
实现文件
@implementation ClassName
//实现协议中的方法
@end
具体实现方法
@protocol MyProtocol <NSObject>
@required//必须实现
-(int) addX:(int) x andY:(int) y;
@optional//可选实现
-(int) substructX:(int) x andY:(int) y;
@end
协议和数据类型
声明实例变量或方法可以指定协议名称
id类型可以指向任何类型的
@interface MyClass
{
id<MyProtocol> myVar;
}
@end
要求传入的参数必须采用MyProtocol协议
-(void) methodName:(id<MyProtocol>) obj
{
//方法代码
}
检查对象是否实现了某个协议
可以通过对象的下列方法来判断是否遵循某协议(confrorms:遵守)
conformsToProtocol:@protocol (协议名)
在此创建一个代理使用的实例
先创建两个类,继承NSObjective
一个被代理的类,命名为HouseMan
一个代理的类,命名Medium
HouseMan.h接口文件中实现
// 房东
#import <Foundation/Foundation.h>
//1.声明协议
@protocol GetHoseProtocol <NSObject>
-(void)getRent;//房租的方法
@end
@interface HouseMan : NSObject
//2.创建一个代理人的属性 在此签订一个协议
@property(nonatomic,assign)id<GetHoseProtocol> delegate;
//3.协议的方法
-(void)houseRent;
@end
HouseMan.m实现文件中实现
//4.实现需要代理的方法
-(void)houseRent
{
[self.delegate getRent];//呼叫被代理人去做
}
Medium.h接口文件中实现
#import <Foundation/Foundation.h>
#import "HouseMan.h"//5.包含代理人的头文件
@interface Medium : NSObject<GetHoseProtocol>//6.被代理人和代理人签订协议
//7声明代理人的属性
@property (nonatomic, strong) NSString *name;
Medium.m实现文件中
#import "Medium.h"
@implementation Medium
//7.实现被代理人的方法
-(void)getRent
{
NSLog(@"正在收房租");
}
@end
最后在main.m文件中
#import <Foundation/Foundation.h>
//包含头文件
#import "HouseMan.h"
#import "Medium.h"
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//创建代理对象
Medium *m=[Medium new];
m.name=@"房屋中介";
//创建被代理对象
HouseMan *h=[HouseMan new];
//给代理对象赋值
h.delegate=m;
//被代理人呼叫代理人实现方法 这个方法是在被代理人种实现的
[h houseRent];
}
return 0;
}
设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
-设计模式是某个具体编程问题的通用化,可以复用的解决方案
-没有一种可以从前一个方案中完全复用代码的情况,但是可以使用前一次的解决问题时使用的思想
-设计模式可以在遇到编程问题时会想起一个正确的解决方案
-常见的设计模式有:单例模式、委托(代理)模式、观察者模式、职责链模式等等
委托模式
在委托模式中,程序中的一个对象代表另外一个对象执行某个动作,或者与之相互协作共同完成某个任务。
Cocoa Touch广泛使用委托(delegate),它是负责为其他对象处理特定任务的类。通过委托,我们能够在某些预定义时间内为发布委托的类做一些工作。
所谓代理,就是指一方授予他方代理权,他方依代理权与第三方进行法律行为,其行为后果由一方承担的一种民事法律制度。委托是一方将一定的事务委诸于另一方实施的法律制度。
委托和代理的区别:
第一,委托规范的是委托人和受托人双方之间的关系;而代理规范的是本人、代理人和第三人的关系。
第二,代理关系中代理人代理的对象是进行意思表示和接受意思表示的行为;而委托中受托人代为实施的行为可以是法律行为,也可以是事实行为。
第三,代理包括对内和对外两种关系,对内是代理人和被代理人之间的关系,而对外是代理人和第三人之间的关系;而委托只是委托人和受托人之间的关系。
简单的总结:
-委托就是有两个对象,一个变化时,另外一个能够知道上面一个的变化.
-一个对象保存另外一个对象的引用,被引用的对象实现事先确定的协议,这个协议用来把对象中的变化通知给被引用的对象
-委托不一定使用到协议,使用类目一样能完成,或者协议类目都不知用,同样能做到.
delegate属性都是assign而不是retain为了避免循环引用造成的内存泄露。
循环引用的问题这样理解:
-比如在main函数中创建了两个类的对象A和B,现在引用计数都是1。现在让A和B互相引用(A有一个属性是B对象,属性说明是retain;B有一个属性是A对象,属性说明是retain),现在两个对象的引用计数都增加了1,都变成了2。
-现在执行 [A release]; [B release]; 此时创建对象的main函数已经释放了自己对对象的所有权,但是此时A和B的引用计数都还是1,因为他们互相引用了。
-这时你发现A和B将无法释放,因为要想释放A必须先释放B,在B的dealloc方法中再释放A。同理,要想释放B必须先释放A,在A的dealloc方法中再释放B。所以这两个对象将一直存在在内存中而不释放。这就是所谓的循环引用的问题。
-要想解决这个问题,一般的方法可以将引用的属性设置为assign,而不是retain来处理。
使用非正式协议的委托模式