Effective Objective-C 2.0 学习记录(一)
八、黑魔法(Method Swizzling)
在runtime时,可以向类中新增或替换选择器所对应的方法实现。这种方法不宜滥用。
用到的相关方法:
method class_getInstanceMethod(Class aClass, SEL aSelector)
//返回aClass的名为aSelector的方法
//如果 swizzling 的是类方法, 采用如下的方式:
Class class = object_getClass((id)self);
...
Method originalMethod = class_getClassMethod(class, originalSelector);
Method swizzledMethod = class_getClassMethod(class, swizzlingSelector);
//添加新方法class_addMethod(,函数指针,对当前函数编码)
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
/*
参数说明:
cls:被添加方法的类
name:可以理解为方法名,
imp:实现这个方法的函数
types:一个定义该函数返回值类型和参数类型的字符串
*/
class_addMethod([EmptyClass class], @selector(say:), (IMP)say, "i@:@");
/*
其中types参数为"i@:@“,按顺序分别表示:
i:返回值类型int,若是v则表示void
@:参数id(self)
::SEL(_cmd)
*/
九、类对象
//每个结构体的首个成员是Class类的变量,该变量定义了对象所属的类,通常称为“is a”指针
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
//此结构体存放类的“元数据”
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;//每个Class都有一个isa指针
#if !__OBJC2__
Class super_class //本类的父类 OBJC2_UNAVAILABLE;
const char *name //类名 OBJC2_UNAVAILABLE;
long version //类版本 OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;//!*!供运行期使用的一些位标识。如:CLS_CLASS (0x1L)表示该类为普通class; CLS_META(0x2L)表示该类为metaclass等(runtime.h中有详细列出)
long instance_size //实例大小 OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars //存储每个实例变量的内存地址
OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;//!*!根据info的信息确定是类还是实例,运行什么函数方法等
struct objc_cache *cache //缓存 OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols //协议 OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
可以用类型信息查询方法来检视类继承体系
isMemberOfClass:判断出对象是否为某个特定类的实例
isKindOfClass:判断出对象是否为某类或攀升累的实例
Teacher *teacher = [[Teacher alloc] init];
[teacher isKindOfClass:[Teacher class]];//yes
[teacher isKindOfClass:[NSObject class]];//yes
[teacher isMemberOfClass:[Teacher class];//yes
[teacher isMemberOfClass:[NSObject class]];//no
注:1、每个实例都有一个指向Class对象的指针,用以表明其类型,而这些Class对象则构成了类的继承体系
2、如果对象类型无法在编译期确定,那么就应该使用类型信息查询来探知
3、尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能
十、理解NSCopying协议
在使用对象时经常需要拷贝它,在oc中此操作通过copy方法完成,如果想令自己的类支持拷贝操作,那就要实现NSCopying协议,该协议有个方法:
-(id)copyWithZone:(NSZone *)zone;
//以前在开发程序时,会据此把内存分成不同的“区”(zone),而创建对象会在某个区里面。
//现在不用了,每个程序只有一个区:“默认区”(default zone)。不必担心zone这个参数。
//.h文件
@interface YCFPerson : NSObject<NSCopying>
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName;
@end
//.m文件
@implementation YCFPerson
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName{
if (self == [super init]) {
_firstName = firstName;
_lastName = lastName;
}
return self;
}
//在当前这个copyWithZone中,我们直接把拷贝的对象交给“指定的初始化方法initWithFirstName”初始化,然而有时候,除了拷贝对象,还要完成其他一些操作,比如类对象中的数据结构可能并未在初始化方法中设置好,需要另行设置。
-(id)copyWithZone:(NSZone *)zone{
//此处一定要通过[self class]方法返回的对象调用allocWithZone:方法。因为指针可能实际指向的是YCFPerson的子类。[self class]就可以返回正确的类的类型对象了。
YCFPerson *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName andLastName:_lastName];
return copy;
}
NSCopying协议与NSMutableCopying的区别主要是在于,返回的对象是否是可变类型的。遵循NSMutableCopying协议,需要实现如下方法
- (id)mutableCopyWithZone:(NSZone *)zone;
//.h文件
@interface YCFSon : NSObject<NSMutableCopying>
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName;
@end
//.m文件
@implementation YCFSon
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName{
if (self == [super init]) {
_firstName = firstName;
_lastName = lastName;
}
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
YCFSon *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName andLastName:_lastName];
return copy;
}
@end
注意:1、若返回一个 不可变的拷贝,需要实现NSCopying,若返回一个可变的拷贝,需要实现NSMutableCopying协议。
2、需要注意是“深拷贝”还是“浅拷贝”。深拷贝就是在拷贝对象自身时,将其底层数据也一并复制过来。浅拷贝只拷贝容器对象本身,而不复制其中的数据。