一.消息机制
OC调用方法是动态调用 调用未实现的方法编译不报错 方法调用的本质是发送消息
import
[p eat];
[p performSelector:@selector(eat)];
objc_msgSend();
Xcode5之后使用运行时可设置
objc_msgSend(p, @selector(eat:),@"");//传递参数
方法调用的本质是 执行performSelector 把一个方法名 转化成一个方法编号 根据这个方法编号去方法列表中查找对应的方法 调用方法的实现
二.交换方法
参考https://www.jianshu.com/p/9e3cf04f3dc8
#import
@interface NSArray (ldd)
@end
#import "NSArray+ldd.h"
#import
@implementation NSArray (ldd)
//const char * const LD_Key = "Key";
+ (void)load
{
[super load];
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(ldd_objectAtIndex:));
method_exchangeImplementations(fromMethod, toMethod);
Method fromMutMethod = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(objectAtIndex:));
Method toMutMethod = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(ldd_mutaObjectAtIndex:));
method_exchangeImplementations(fromMutMethod, toMutMethod);
}
- (id)ldd_objectAtIndex:(NSUInteger)index
{
if (index < self.count) {
return [self ldd_objectAtIndex:index];
}else{
return nil;
}
}
- (id)ldd_mutaObjectAtIndex:(NSUInteger)index
{
if (index < self.count) {
return [self ldd_mutaObjectAtIndex:index];
}else{
return nil;
}
}
@end
三.动态添加方法
Person * p = [[Person alloc]init];
//[p eat]; //编译不过
//动态添加方法
[p performSelector:@selector(eat) withObject:nil];//编译的过 运行崩
//
// Person.m
// test
//
// Created by 李洞洞 on 27/12/17.
// Copyright © 2017年 Minte. All rights reserved.
//
#import "Person.h"
#import
@implementation Person
void ldd(id ldself,SEL ldsel){
NSLog(@"这是一个动态添加的方法");
NSLog(@"%@ -- %@",[ldself class],NSStringFromSelector(ldsel));
};
//调用时刻:当在外界调用了当前类未实现的方法时调用
+ (BOOL)resolveClassMethod:(SEL)sel
{
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == NSSelectorFromString(@"eat")) {
//IMP:方法的实现 函数的入口 函数名 函数指针
//types:方法的类型
class_addMethod([self class], @selector(eat), (IMP)ldd, "V@:");
}
return [super resolveInstanceMethod:sel];
}
@end
以上图中可以通过
NSLog(@"%s",@encode(id));
来获取
或者用
Method method = class_getInstanceMethod(self,@selector(other));
method_getTypeEncoding(method);
#pragma mark -- 动态添加方法
Person * p = [[Person alloc]init];
[p performSelector:@selector(eat:) withObject:@""];
#import "Person.h"
#import
@implementation Person
void ldd(id ldself,SEL ldsel,id some){
NSLog(@"这是一个动态添加的方法");
NSLog(@"%@ -- %@",[ldself class],NSStringFromSelector(ldsel));
};
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == NSSelectorFromString(@"eat:")) {
class_addMethod([self class], @selector(eat:), (IMP)ldd, "V@:@");
}
return [super resolveClassMethod:sel];
}
四.动态添加属性
#import
@interface UIButton (ldd)
/*
不生成_ld_Title
不生成 ld_Title的set get
*/
@property NSString * ld_Title;
@end
#import "UIButton+ldd.h"
#import
@implementation UIButton (ldd)
const char * const LD_Key = "Key";
- (void)setLd_Title:(NSString *)ld_Title
{
objc_setAssociatedObject(self, LD_Key, ld_Title, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString*)ld_Title
{
return objc_getAssociatedObject(self, LD_Key);
}
@end
如果不用以上的写法 手动实现set get方法也可以 不好的地方是:
静态全局变量来保存值 如果我这个对象销毁了 这个变量保存的值还在 此时你变量保存的值和对象之间并没任何关系 和我这个对象无关了。 最好的状态是我这个对象在 这个属性在 对象不在 属性也不在。
#pragma mark -- 动态添加属性
UIButton * btn = [[UIButton alloc]init];
btn.ld_Title = @"ldd";
NSLog(@"%@",btn.ld_Title);
btn.ld_Title = @"999";
NSLog(@"%@",btn.ld_Title);
或者这么玩
static char * const LD_Key = "Key";
static char * const LD_ButtonKey = "ButtonKey";
- (void)viewDidLoad {
[super viewDidLoad];
UIButton * button = ({
UIButton * btn = [[UIButton alloc]init];
objc_setAssociatedObject(btn, LD_ButtonKey, @"6666", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
btn;
});
[self.view addSubview:button];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",objc_getAssociatedObject(button, LD_ButtonKey));
});
UIView * view1 = ({
UIView * view = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
view.backgroundColor = [UIColor redColor];
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(view1Click:)];
NSDictionary * value= @{@"color":[UIColor greenColor]};
objc_setAssociatedObject(view, LD_Key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[view addGestureRecognizer:tap];
view;
});
[self.view addSubview:view1];
}
- (void)view1Click:(UITapGestureRecognizer*)tap
{
UIView * view = tap.view;
NSDictionary *value = objc_getAssociatedObject(view, LD_Key);
view.backgroundColor = value[@"color"];
}
@end
五.字典转模型
#import
@protocol LDModel
@optional
/**
模型字段是数组
@return {模型字段 : 数组中存储的自定义模型的类名}
*/
+ (NSDictionary *)objectClassInArray;
/**
模型中特殊字段(id...)的替换
@return {模型中的字段:字典中的特殊字段}
*/
+ (NSDictionary *)propertykeyReplacedWithValue;
@end
@interface NSObject (Prepory)
+ (id)modelWithDict:(NSDictionary*)dict;
@end
#import "NSObject+Prepory.h"
#import
@implementation NSObject (Prepory)
+ (id)modelWithDict:(NSDictionary *)dict
{
id object = [[self alloc]init];
unsigned int count = 0;
Ivar * ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
char const * name = ivar_getName(ivar);
NSString * ocName = [NSString stringWithUTF8String:name];
NSString * key = [ocName substringFromIndex:1];
id value = nil;
if ([self respondsToSelector:@selector(propertykeyReplacedWithValue)]) {
key = [[self propertykeyReplacedWithValue]objectForKey:key];
if (!key) {
key = [ocName substringFromIndex:1];
}
value = dict[key];
}
key = [ocName substringFromIndex:1];
const char * name1 = ivar_getTypeEncoding(ivar);
NSString * newName = [[NSString alloc]initWithUTF8String:name1];
if ([value isKindOfClass:[NSDictionary class]]&&![key hasPrefix:@"NS"]) {
newName = [newName stringByReplacingOccurrencesOfString:@"\"" withString:@""];
newName = [newName stringByReplacingOccurrencesOfString:@"@" withString:@""];
Class class = NSClassFromString(newName);
id val = [class modelWithDict:value];
[object setValue:val forKey:key];
}else if ([value isKindOfClass:[NSArray class]]&&![key hasPrefix:@"NS"]){
if ([self respondsToSelector:@selector(objectClassInArray)]) {
id propertyValueType = [[self objectClassInArray] objectForKey:key];
Class clas = NSClassFromString(propertyValueType);
NSArray * arr = (NSArray*)value;
id obj = nil;
NSMutableArray * muArr = [NSMutableArray array];
for (int i = 0; i < arr.count; i++) {
obj = [clas modelWithDict:arr[i]];//key里面放的是区县模型//这是一个模型
[muArr addObject:obj];
}
[object setValue:muArr forKey:key];
}
}else if (value) {
[object setValue:value forKey:key];
}
}
free(ivarList);
return object;
}
@end
六.KVO的底层实现
当某个类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个派生类,在这个派生类中重写基类的任何被观察属性的setter方法。派生类在被重写的setter方法内实现真正的通知机制
如果原类为Person,那么生成的派生类名为NSKVONotifying_Person。
每一个类中都有一个isa指针指向当前类,所有系统就是在当一个类的对象第一次被观察的时候,系统就会将isa指针指向动态生成的派生类,从而在被监听属性赋值时被执行的是派生类的setter方法
键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而 observeValueForKey: ofObject: change: context: 也会被调用。
七.归档&解档
#import
@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *height;
@end
#import "Person.h"
#import
@implementation Person
- (void)encodeWithCoder:(NSCoder *)aCoder
{
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
NSString * key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
//归档
[aCoder encodeObject:value forKey:key];
}
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
NSString * key = [NSString stringWithUTF8String:name];
//解档
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
}
return self;
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
Person * p = [[Person alloc]init];
p.name = @"ldd";
p.age = @"26";
p.height = @"1.80";
NSString * filePath = [@"/Users/fangduozhang/Desktop" stringByAppendingPathComponent:@"LDD.data"];
[NSKeyedArchiver archiveRootObject:p toFile:filePath];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
Person * p = [NSKeyedUnarchiver unarchiveObjectWithFile:[@"/Users/fangduozhang/Desktop" stringByAppendingPathComponent:@"LDD.data"]];
NSLog(@"%@---%@---%@",p.name,p.age,p.height);
}
八.数据库动态方案
https://github.com/Buliceli/LDnamicDataBase
鉴于能力有限 水平一般 理解有误之处 望大侠不吝指出 ThankYou!