一.消息機(jī)制
OC調(diào)用方法是動(dòng)態(tài)調(diào)用 調(diào)用未實(shí)現(xiàn)的方法編譯不報(bào)錯(cuò) 方法調(diào)用的本質(zhì)是發(fā)送消息
import <objc/message.h>
[p eat];
[p performSelector:@selector(eat)];
objc_msgSend();
Xcode5之后使用運(yùn)行時(shí)可設(shè)置
objc_msgSend(p, @selector(eat:),@"??");//傳遞參數(shù)

方法調(diào)用的本質(zhì)是 執(zhí)行performSelector 把一個(gè)方法名 轉(zhuǎn)化成一個(gè)方法編號 根據(jù)這個(gè)方法編號去方法列表中查找對應(yīng)的方法 調(diào)用方法的實(shí)現(xiàn)
二.交換方法
參考http://www.itdecent.cn/p/9e3cf04f3dc8
#import <Foundation/Foundation.h>
@interface NSArray (ldd)
@end
#import "NSArray+ldd.h"
#import <objc/message.h>
@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
三.動(dòng)態(tài)添加方法
Person * p = [[Person alloc]init];
//[p eat]; //編譯不過
//動(dòng)態(tài)添加方法
[p performSelector:@selector(eat) withObject:nil];//編譯的過 運(yùn)行崩
//
// Person.m
// test
//
// Created by 李洞洞 on 27/12/17.
// Copyright ? 2017年 Minte. All rights reserved.
//
#import "Person.h"
#import <objc/message.h>
@implementation Person
void ldd(id ldself,SEL ldsel){
NSLog(@"這是一個(gè)動(dòng)態(tài)添加的方法");
NSLog(@"%@ -- %@",[ldself class],NSStringFromSelector(ldsel));
};
//調(diào)用時(shí)刻:當(dāng)在外界調(diào)用了當(dāng)前類未實(shí)現(xiàn)的方法時(shí)調(diào)用
+ (BOOL)resolveClassMethod:(SEL)sel
{
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == NSSelectorFromString(@"eat")) {
//IMP:方法的實(shí)現(xiàn) 函數(shù)的入口 函數(shù)名 函數(shù)指針
//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 -- 動(dòng)態(tài)添加方法
Person * p = [[Person alloc]init];
[p performSelector:@selector(eat:) withObject:@"??"];
#import "Person.h"
#import <objc/message.h>
@implementation Person
void ldd(id ldself,SEL ldsel,id some){
NSLog(@"這是一個(gè)動(dòng)態(tài)添加的方法");
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];
}
四.動(dòng)態(tài)添加屬性
#import <UIKit/UIKit.h>
@interface UIButton (ldd)
/*
不生成_ld_Title
不生成 ld_Title的set get
*/
@property NSString * ld_Title;
@end
#import "UIButton+ldd.h"
#import <objc/message.h>
@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
如果不用以上的寫法 手動(dòng)實(shí)現(xiàn)set get方法也可以 不好的地方是:
靜態(tài)全局變量來保存值 如果我這個(gè)對象銷毀了 這個(gè)變量保存的值還在 此時(shí)你變量保存的值和對象之間并沒任何關(guān)系 和我這個(gè)對象無關(guān)了。 最好的狀態(tài)是我這個(gè)對象在 這個(gè)屬性在 對象不在 屬性也不在。
#pragma mark -- 動(dòng)態(tài)添加屬性
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
五.字典轉(zhuǎn)模型
#import <Foundation/Foundation.h>
@protocol LDModel <NSObject>
@optional
/**
模型字段是數(shù)組
@return {模型字段 : 數(shù)組中存儲(chǔ)的自定義模型的類名}
*/
+ (NSDictionary *)objectClassInArray;
/**
模型中特殊字段(id...)的替換
@return {模型中的字段:字典中的特殊字段}
*/
+ (NSDictionary *)propertykeyReplacedWithValue;
@end
@interface NSObject (Prepory)<LDModel>
+ (id)modelWithDict:(NSDictionary*)dict;
@end
#import "NSObject+Prepory.h"
#import <objc/message.h>
@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里面放的是區(qū)縣模型//這是一個(gè)模型
[muArr addObject:obj];
}
[object setValue:muArr forKey:key];
}
}else if (value) {
[object setValue:value forKey:key];
}
}
free(ivarList);
return object;
}
@end
六.KVO的底層實(shí)現(xiàn)
當(dāng)某個(gè)類的屬性對象第一次被觀察時(shí),系統(tǒng)就會(huì)在運(yùn)行期間動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類,在這個(gè)派生類中重寫基類的任何被觀察屬性的setter方法。派生類在被重寫的setter方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制
如果原類為Person,那么生成的派生類名為NSKVONotifying_Person。
每一個(gè)類中都有一個(gè)isa指針指向當(dāng)前類,所有系統(tǒng)就是在當(dāng)一個(gè)類的對象第一次被觀察的時(shí)候,系統(tǒng)就會(huì)將isa指針指向動(dòng)態(tài)生成的派生類,從而在被監(jiān)聽屬性賦值時(shí)被執(zhí)行的是派生類的setter方法
鍵值觀察通知依賴于NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey:;在一個(gè)被觀察屬性發(fā)生改變之前, willChangeValueForKey: 一定會(huì)被調(diào)用,這就 會(huì)記錄舊的值。而當(dāng)改變發(fā)生后,didChangeValueForKey: 會(huì)被調(diào)用,繼而 observeValueForKey: ofObject: change: context: 也會(huì)被調(diào)用。
七.歸檔&解檔
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCoding>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *height;
@end
#import "Person.h"
#import <objc/message.h>
@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<UITouch *> *)touches withEvent:(UIEvent *)event
{
Person * p = [NSKeyedUnarchiver unarchiveObjectWithFile:[@"/Users/fangduozhang/Desktop" stringByAppendingPathComponent:@"LDD.data"]];
NSLog(@"%@---%@---%@",p.name,p.age,p.height);
}
八.數(shù)據(jù)庫動(dòng)態(tài)方案
https://github.com/Buliceli/LDnamicDataBase
鑒于能力有限 水平一般 理解有誤之處 望大俠不吝指出 ThankYou!