类别与扩展
类别
类别可以对已有类添加方法,类别中通常指定义方法,一般接口文件名为 已有类+类别名.h
@interface 已有类 (类别名)
//定义方法
@end
@implementation 已有类 (类别名)
//实现方法
@end
通过类别为指定类添加新方法后,其子类也会获取新添加的方法
可对一个指定类添加多个类别
通过类别调用私有方法
私有方法:没有在接口部分中定义,只在实现部分中定义了的方法,由于不能直接被类或实例调用,相当于私有方法
私有方法可用 performSelector: 来执行调用,但这样完全避开了编译器的语法检查,不是一种好的方法,这里也可以通过类别在接口部分定义私有方法,这样即可正常调用私有方法
扩展
相当于匿名类别,可额外增加实例变量,也可用 @property、@snythesize 来合成 setter、getter 方法,但定义类的列表是,则不允许额外定义实例变量。
协议(protocol)与委托
使用类别实现非正式协议
通过对 NSObject 创建类别新增方法,这样当某个类实现 NSObject 的该类别时就需要实现该类别下的所有方法,这种基于 NSObject 的类别即可认为是非正式协议。
//基于 NSObject 定义类别 Eatable
@interface NSObject (Eatable)
- (void) taste;
@end
//定义接口部分
@interface MyApple: NSObject
@end
//定义实现部分
@implementation MyApple
- (void) taste
{
NSLog(@"苹果好吃!");
}
@end
//main()部分
MyApple* apple = [[MyApple alloc] init];
[apple taste]; //输出:苹果好吃!
正式协议的定义与实现
@protocol 协议名 <父协议1, 父协议2...>
{
//零到多个方法定义
}
@interface 类名: 父类 <协议名a, 协议名b...>
{
//实现协议
}
定义协议时只需定义方法即可,实现是在定义类接口部分中
使用协议定义变量时:
- NSObject<协议1, 协议2…>* 变量名;
- id<协议1, 协议2…>* 变量名;
使用协议定义变量的话,那么这些变量就只能调用协议中的变量(参考多态)
// MyOutput 为协议名,out 为协议变量,MyPrinter 为实现了 MyOutput 协议的类,所以这个 out 变量可编译成功,但只能调用 MyOutput 中定义并且在 MyPrinter 实现部分中定义的方法,不能调用 MyPrinter 中其他的方法
id<MyOutput> out = [[MyPrinter alloc] init];
使用 @try 处理异常
int main()
{
@autoreleasepool
{
@try
{
MyApple* app = [[NyApple alloc] init];
[app taste];
}
@cathch(NSException* ex)
{
NSLog(@"--捕捉异常--");
NSLog(@"Exception name:%@,exception reason:%@。", ex.name, ex.reason);
}
@finally
{
NSLog(@"资源回收");
}
NSLog(@"程序结束");
}
}
@try用来包含可能出现exception的代码
@catch用来捕获exception,可设置多个@catch块,出现exception时,会依次寻找@catch块,当exception对象与某@catch对象后的exception类或其子类匹配上之后,则会用该@catch块来处理exception,所以对于NSException类(所有异常类均继承于此)对应的@catch块应该放在最后,以符合先子类,后父类的原则
exception对象包含name、reason、userInfo(返回用户附加信息,返回值是一个NSDictionary对象)三个常用方法。
@finally用来回收资源,位与@try和@catch块之后,且@finally块总是会执行的,所以@finally块中最好不要有return或@throw语句,否则会导致@try块中的return、@throw语句失效
抛出异常与自定义异常类
//自定义异常类以及MyDog类
@interface MyException: NSException
@end
@interface MyDog: NSObject
@property (nonatomic, assign) int age;
@end
//实现部分
@implementation MyException
@end
@implementation MyDog
@snythesize age = _age;
- (void) setAge:(int) age
{
if(age > 50 || age < 0)
{
@throw [[MyException alloc]
initWithName:@"UkkegakArgymentException"
reason:@"dog's age need to be controlled between 0 and 50!"
userInfo:nil];
}
_age = age;
}
OC反射机制
获得Class
//通过字符串用 Class NSClassFromString(NSString* className) 函数获取Class
Class clazz1 = NSClassFromString(@"NSDate");
NSLog(@"%@", clazz1); //输出结果为 NSDate
//用clazz1来创建对象
id date = [[clazz1 alloc] init];
NSLog(@"%@", date); //输出结果是当下时间
NSLog(@"%@", [clazz1 class]); //通过对象的class方法来获取Class并输出,输出结果为 _NSDate
NSLog(@"%@", clazz1 == NSDate.class); //通过类的class方法来获取Class并比较,输出结果为 1
PS: 关于 [date class] 的结果为 _NSDate 而非 NSDate 的原因,是因为 NSDate 只是类簇的前端,初始化创建对象时实际返回的是 NSDate 得子类(_NSDate)实例,而不是 NSDate 的实例。
检查继承关系
- isKindOfClass: 传入class参数判断是否为该类及其子类的实例
- isMemberOfClass: 传入class参数判断是否为该类的实例
- conformsToProtocol: 传入protocol参数判断是否为该类及其子类的实例
获取Protocol参数方法:
- @protocol指令
- Protocol NSProtocolFromString(NSString name) 方法
MyApple* app = [[MyApple alloc] init];
NSLog(@"The class app belong to is: %@", [app class]);
NSLog(@"Does app belong to MyApple Class: %d", [app isMemberOfClass: MyApple.class]);
NSLog(@"Does app belong to NSObject Class: %d", [app isMemberOfClass: NSClassFromString(@"NSObject"));
NSLog(@"Does app belong to MyApple Class or its Child Class: %d", [app isKindOfClass: NSClassFromString(@"MyApple")]);
NSLog(@"Does app belong to NSObject Class or its Child Class: %d", [app isKindOfClass: NSObject.class]);
NSLog(@"1.Does app implement MyEatable protocol:%d", [app conformsToProtocol: @protocol(MyEatable)]);
NSLog(@"2.Does app implement MyEatable protocolL %d", [app conformsToProtocol: *NSProtocolFromString(@"MyEatable")]);
动态调用方法
- 通过 performSelector: 方法方法实现动态调用方法,需要传入 SEL 对象,若需要参数则可通过 withObject: 传入参数
- 通过 objc_mshSend(receiver, selector, …),第一个参数为调用者,第二个参数为方法,之后为调用方法所需的参数
PS: 使用 objc_msgSend() 应该 #import < objc/message.h > ,另外还有两个版本,即objc_msgSend_fpret()和objc_msgSend_stret(),前者用于返回浮点数,后者用于返回结构体。
获取 selector 参数;
- 使用 @selector指令来获取当前类中指定的方法,该指令需要完整的方法签名关键字作为参数,仅有方法名是不够的。
- 使用 SEL NSSelectorFromString(NSString* SelectorName)
id car = [[NyCar alloc] init];
//使用 performSelector: 方法来动态调用方法
[car performSelector:@selector(addSpeed:) withObject:3.4];
//使用 objc_msgSend() 函数动态调用方法
objc_msgSend(car, NSSelectorFromString(@"addSpeed:"), 3.4);
//定义函数指针变量
double (*addSpeed)(id, SEL, double);
//获取 car 对象的 addSpeed: 方法,病赋值给 addSpeed 函数指针变量
addSpeed = (double(*)(id, SEL, double))[car methodForSelector:@selector(addSpeed:)];
//通过 addSpeed 函数指针变量来调用 car 对象的方法
double speed = addSpeed(car, @selector(addSpeed:),m 3.4);
//输出调用方法的返回值
NSLog(@"speed is: %g", speed);
本文介绍了Objective-C中的高级特性,包括类别、扩展、协议、委托、异常处理、反射机制等,并提供了详细的代码示例。
1908

被折叠的 条评论
为什么被折叠?



