浅拷贝只是将指针赋值,而深拷贝进行了内容传递,在Objective-C中,NSObject的拷贝方式有两种:copy和mutablecopy;
对于NSString,NSArray这类不可变对象:
(1)使用copy得到的结果为浅拷贝,个人理解为:由于这类对象不能更改,它们在内存中存储的形式都是一致的,因此copy操作没有必要重新分配空间来存储;
(2)使用mutablecopy得到的结果为深拷贝,以NSString为例,当发生mutablecopy,得到的返回值类型为NSMutableString,因此需要在内存中重新分配空间进行存储;
对于NSMutableString、NSMutableArray这类可变对象,使用copy或者mutablecopy,都是在进行深拷贝;
以NSDictionary为例,进行深拷贝;
NSDictionary* copyWith(NSDictionary* dic) {
NSMutableDictionary* result = [[NSMutableDictionary alloc] init];
for (NSString* i in [dic allKeys]) {
id copyValue = [i mutableCopy];
[result setObject:copyValue forKey:i];
}
return result;
}
在OC中,消息转发机制如下,通过重写函数,可以实现selector not found防护;
(1) 重写resolveInstanceMethod
在运行时添加未声明的方法,然后再执行;
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(run)) {
class_addMethod(self, sel, (IMP)run, @encode(NSNumber*(*)(id self, SEL _cmd, id i)));
return YES;
}
return [super resolveInstanceMethod:sel];
}
通过class_addMethods,手动添加类方法,然后return YES,重新执行;
(2)重写towardingTargetForSelector
采用重定向的方式,由其他具有这个方法的对象来执行该方法;
- (id) forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(identifier)) {
return _people;
}
return nil;
}
(3) 重写fowardInvocation方法
首先重写methodSignatureForSelector,生成函数签名,记录了这个函数的返回类型,调用者类型,参数类型等信息,以便寄存器操作,然后重写fowardInvocation方法,直接执行;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(foo)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ( [_people respondsToSelector: [anInvocation selector]] ) {
[anInvocation invokeWithTarget: _people];
}
}
执行代码:
@interface People : NSObject
-(void) identifier;
@end
@implementation People
-(void) identifier {
NSLog(@"\nCalled by People.\n");
}
-(void) foo {
NSLog(@"\nfoo!\n");
}
@end
@interface Dog: NSObject
@property (nonatomic, retain) People* people;
@property (nonatomic, retain) NSString* str1;
@end
@implementation Dog
- (instancetype)init
{
_people = [[People alloc] init];
_str1 = [[NSString alloc] init];
return self;
}
+ (Dog*) deepCopy: (Dog*)aDog
{
Dog* result = [[Dog alloc] init];
[result setValue:[[People alloc] init] forKey:@"people" ];
[result setValue:[ [aDog str1] mutableCopy] forKey: @"str1"];
return result;
}
- (void) eat
{
NSLog(@"\ncalled by Dog\n");
}
- (id) forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(identifier)) {
return _people;
}
return nil;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(run)) {
class_addMethod(self, sel, (IMP)run, @encode(NSNumber*(*)(id self, SEL _cmd, id i)));
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ( [_people respondsToSelector: [anInvocation selector]] ) {
[anInvocation invokeWithTarget: _people];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(foo)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
@end
......
Dog* dog = [[Dog alloc] init];
[dog performSelector:@selector(eat)];
[dog performSelector:@selector(identifier)];
[dog performSelector:@selector(run) withObject:[NSNumber numberWithInt:1]];
[dog performSelector:@selector(foo)];
// 练习用Runtime新增一个类
Class myClass = objc_allocateClassPair([NSObject class], "MyClass", 0);
class_addIvar(myClass, "var1", sizeof(NSString*), 0, @encode(NSString *));
class_addMethod(myClass, @selector(run), (IMP)run, @encode(NSNumber*(*)(id self, SEL _cmd, id i)) );
id myObj = [[myClass alloc] init];
[myObj setValue:@"123" forKey:@"var1"];
return 0;
记录点:
(1)使用objc_allocateClassPair,新增一个类;
(2)使用class_addIvar,添加类的成员变量;
(3)使用ckass_addMethod,添加成员方法;
(4)在class_addMethod中,使用@encode,根据方法格式,生成函数签名;
(5)使用performSelector执行方法时,返回值必须为NSObject类型,这是由它的声明决定的;
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;