前言
需求是自定义的一个类需要实现mutableCopy。这样操作就保证数据一样的前提下,修改这个类就不会相互影响了。
一、要实现mutableCopy的前提
**注意!**要实现mutableCopy的话这个类需要遵守NSMutableCopying 协议,不遵守直接调用 [类名 mutableCopy]的话会造成下方提示的方法未找到的崩溃。
[Message mutableCopyWithZone:]: unrecognized selector sent to instance 0x282326340
所有我们就需要遵守以下协议
@interface Message : NSObject <NSMutableCopying>
二、实现代码
话不多少上代码 ✧(≖ ◡ ≖), 在.m中实现下面的方法。
注:Message是我自己创建的类,如果他是其他类麻烦请自行进行替换就行了。
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
Message * objCopy = [[Message alloc] init];
unsigned int count;
//得到这个类的属性数量以及这个类声明的属性
objc_property_t * properties = class_copyPropertyList(object_getClass(objCopy), &count);
NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
//遍历属性名称并添加到数组中
for (unsigned int i = 0; i < count; i++) {
const char* propertyName = property_getName(properties[i]);
[propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
}
//释放 objc_property_t
free(properties);
//使用KVC赋值
for (int i = 0; i < count ; i++)
{
NSString *name=[propertyArray objectAtIndex:i];
id value=[self valueForKey:name];
if([value respondsToSelector:@selector(mutableCopyWithZone:)]){
[objCopy setValue:[value mutableCopy] forKey:name];
}
else{
}
}
return objCopy;
}
三、分析代码
1 Message * objCopy = [[Message alloc] init];
2 unsigned int count;
这两行我就不进行解释了
3 objc_property_t * properties = class_copyPropertyList(object_getClass(objCopy), &count);
这第三行他到底做了什么呢?于是我去苹果官方开放的源码中去找,找到objc-runtime-new.mm这个文件。有人会不会问为啥objc-runtime-new 而不是objc-runtime-old呢?objc-runtime-old 是老的啦,被抛弃的╮(╯▽╰)╭。所以这里就不看old文件了,直接看new文件。
于是在文件中就可以发现这段代码
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
checkIsKnownClass(cls);
assert(cls->isRealized());
auto rw = cls->data();
property_t **result = nil;
unsigned int count = rw->properties.count();
if (count > 0) {
result = (property_t **)malloc((count + 1) * sizeof(property_t *));
count = 0;
for (auto& prop : rw->properties) {
result[count++] = ∝
}
result[count] = nil;
}
if (outCount) *outCount = count;
return (objc_property_t *)result;
}
原来是用来获取这个类的属性数量以及这个类声明的属性。会返回一个objc_property_t 的结构体。
count 就是属性数量
result 里面就描述了类声明的属性。
咱们顺便看看 objc_property_t 这个结构体里面有哪些东东
struct property_t {
const char *name;
const char *attributes;
};
一个name和attributes…
接着我们看
4 NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
5 for (unsigned int i = 0; i < count; i++) {
6 const char* propertyName = property_getName(properties[i]);
7 [propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
}
我们可以根据class_copyPropertyList这个方法获取到的数量来做一个循环
const char *property_getName(objc_property_t prop)
{
return prop->name;
}
property_getName 其实就是获取了 objc_property_t 这个结构体当中的name。不过注意这个name 是char 类型的,我们需要转换一下,转成NSString,方面我们使用。所以就需要下面这个方法啦
[propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
接下来是很重要的第九行
9 free(properties);
这一行properties必须释放,否则会造成内存泄露等,苹果官方注释也提到这么一句话:
Caller must free the block.
后面几行我就不看了,就是利用KVC进行赋值。
至此就分析结束了。
四、结束语
本人也在不断的学习以及摸索中,如有不对的地方欢迎指出,在下方回复或者邮件都可以,再此感谢大家的阅读。
邮箱:zzx986661689@gmail.com