样例Demo

欢迎给我star!我会继续分享的。

概述

Objc Runtime使得C具有了面向对象能力,在程序执行时创建,检查。改动类、对象和它们的方法。Runtime是C和汇编编写的,这里http://www.opensource.apple.com/source/objc4/能够下到苹果维护的开源码,GNU也有一个开源的runtime版本号。他们都努力的保持一致。

应用场景

  1. 将某些OC代码转为执行时代码,探究底层,比方block的实现原理
  2. 拦截系统自带的方法调用(Swizzle 黑魔法),比方拦截imageNamed:、viewDidLoad、alloc
  3. 实现分类也能够添加属性
  4. 实现NSCoding的自己主动归档和自己主动解档
  5. 实现字典和模型的自己主动转换。

    (MJExtension)

  6. 修BUG神器,假设大型框架的BUG 通过Runtime来解决,非常好用。

一些经常使用类型

Method

Method

An opaque type that represents a method in a class definition.

Declaration

typedef struct objc_method *Method;

代表类定义中的方法的不透明类型。

Class

Class

An opaque type that represents an Objective-C class.

Declaration

typedef struct objc_class *Class;

代表Objective-C中的类

Ivar

An opaque type that represents an instance variable.

Declaration

typedef struct objc_ivar *Ivar;

代表实例变量

IMP

IMP

A pointer to the start of a method implementation.

指向方法实现的開始的内存地址的指针。

SEL

SEL

Defines an opaque type that represents a method selector.

Declaration

typedef struct objc_selector *SEL;

代表方法的选择器

类与对象操作函数

runtime有非常多的函数能够操作类和对象。

类相关的是class为前缀,对象相关操作是objc或object_为前缀。

类相关操作函数

name

// 获取类的类名

const char * class_getName ( Class cls );

super_class和meta-class

// 获取类的父类

Class class_getSuperclass ( Class cls );

// 推断给定的Class是否是一个meta class

BOOL class_isMetaClass ( Class cls );

instance_size

// 获取实例大小

size_t class_getInstanceSize ( Class cls );

成员变量(ivars)及属性

//成员变量操作函数
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name ); // 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name ); // 加入成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); //这个仅仅能够向在runtime时创建的类加入成员变量 // 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); //必须使用free()来释放这个数组 //属性操作函数
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name ); // 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name ); // 加入成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); // 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );

methodLists


// 加入方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types ); //和成员变量不同的是能够为类动态加入方法。假设有同名会返回NO,改动的话须要使用method_setImplementation // 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name ); // 获取类方法
Method class_getClassMethod ( Class cls, SEL name ); // 获取全部方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount ); // 替代方法的实现
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types ); // 返回方法的详细实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name ); // 类实例是否响应指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );

objc_protocol_list

// 加入协议
BOOL class_addProtocol ( Class cls, Protocol *protocol ); // 返回类是否实现指定的协议
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol ); // 返回类实现的协议列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );

version

// 获取版本号号
int class_getVersion ( Class cls ); // 设置版本号号
void class_setVersion ( Class cls, int version );

实例

//-----------------------------------------------------------
// MyClass.h
@interface MyClass : NSObject <NSCopying, NSCoding>
@property (nonatomic, strong) NSArray *array;
@property (nonatomic, copy) NSString *string;
- (void)method1;
- (void)method2;
+ (void)classMethod1;
@end //-----------------------------------------------------------
// MyClass.m
#import "MyClass.h"
@interface MyClass () {
NSInteger _instance1;
NSString * _instance2;
}
@property (nonatomic, assign) NSUInteger integer;
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2;
@end @implementation MyClass
+ (void)classMethod1 {
} - (void)method1 {
NSLog(@"call method method1");
} - (void)method2 {
} - (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2 {
NSLog(@"arg1 : %ld, arg2 : %@", arg1, arg2);
} @end //-----------------------------------------------------------
// main.h #import "MyClass.h"
#import "MySubClass.h"
#import <objc/runtime.h> int main(int argc, const char * argv[]) {
@autoreleasepool {
MyClass *myClass = [[MyClass alloc] init];
unsigned int outCount = 0;
Class cls = myClass.class;
// 类名
NSLog(@"class name: %s", class_getName(cls));
NSLog(@"=========================================================="); // 父类
NSLog(@"super class name: %s", class_getName(class_getSuperclass(cls)));
NSLog(@"=========================================================="); // 是否是元类
NSLog(@"MyClass is %@ a meta-class", (class_isMetaClass(cls) ? @"" : @"not"));
NSLog(@"=========================================================="); Class meta_class = objc_getMetaClass(class_getName(cls));
NSLog(@"%s's meta-class is %s", class_getName(cls), class_getName(meta_class));
NSLog(@"=========================================================="); // 变量实例大小
NSLog(@"instance size: %zu", class_getInstanceSize(cls));
NSLog(@"=========================================================="); // 成员变量
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
} free(ivars); Ivar string = class_getInstanceVariable(cls, "_string");
if (string != NULL) {
NSLog(@"instace variable %s", ivar_getName(string));
} NSLog(@"=========================================================="); // 属性操作
objc_property_t * properties = class_copyPropertyList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSLog(@"property's name: %s", property_getName(property));
} free(properties); objc_property_t array = class_getProperty(cls, "array");
if (array != NULL) {
NSLog(@"property %s", property_getName(array));
} NSLog(@"=========================================================="); // 方法操作
Method *methods = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Method method = methods[i];
NSLog(@"method's signature: %s", method_getName(method));
} free(methods); Method method1 = class_getInstanceMethod(cls, @selector(method1));
if (method1 != NULL) {
NSLog(@"method %s", method_getName(method1));
} Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
if (classMethod != NULL) {
NSLog(@"class method : %s", method_getName(classMethod));
} NSLog(@"MyClass is%@ responsd to selector: method3WithArg1:arg2:", class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @" not"); IMP imp = class_getMethodImplementation(cls, @selector(method1));
imp(); NSLog(@"=========================================================="); // 协议
Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
Protocol * protocol;
for (int i = 0; i < outCount; i++) {
protocol = protocols[i];
NSLog(@"protocol name: %s", protocol_getName(protocol));
} NSLog(@"MyClass is%@ responsed to protocol %s", class_conformsToProtocol(cls, protocol) ? @"" : @" not", protocol_getName(protocol)); NSLog(@"==========================================================");
}
return 0;
}

动态创建类和对象

动态创建类

// 创建一个新类和元类
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes ); //假设创建的是root class,则superclass为Nil。extraBytes通常为0 // 销毁一个类及其相关联的类
void objc_disposeClassPair ( Class cls ); //在执行中还存在或存在子类实例,就不能够调用这个。 // 在应用中注冊由objc_allocateClassPair创建的类
void objc_registerClassPair ( Class cls ); //创建了新类后,然后使用class_addMethod。class_addIvar函数为新类加入方法,实例变量和属性后再调用这个来注冊类,再之后就能够用了。

使用实例

Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "v@:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i"); objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar}; class_addProperty(cls, "property2", attrs, 3);
objc_registerClassPair(cls); id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];

动态创建对象

// 创建类实例
id class_createInstance ( Class cls, size_t extraBytes ); //会在heap里给类分配内存。这种方法和+alloc方法相似。 // 在指定位置创建类实例
id objc_constructInstance ( Class cls, void *bytes ); // 销毁类实例
void * objc_destructInstance ( id obj ); //不会释放移除不论什么相关引用

測试下效果

//能够看出class_createInstance和alloc的不同
id theObject = class_createInstance(NSString.class, sizeof(unsigned));
id str1 = [theObject init];
NSLog(@"%@", [str1 class]);
id str2 = [[NSString alloc] initWithString:@"test"];
NSLog(@"%@", [str2 class]);

实例操作函数

这些函数是针对创建的实例对象的一系列操作函数。

整个对象操作的函数

// 返回指定对象的一份拷贝
id object_copy ( id obj, size_t size );
// 释放指定对象占用的内存
id object_dispose ( id obj );

应用场景

//把a转换成占用很多其它空间的子类b
NSObject *a = [[NSObject alloc] init];
id newB = object_copy(a, class_getInstanceSize(MyClass.class));
object_setClass(newB, MyClass.class);
object_dispose(a);

对象实例变量进行操作的函数

// 改动类实例的实例变量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 获取对象实例变量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向给定对象分配的不论什么额外字节的指针
void * object_getIndexedIvars ( id obj );
// 返回对象中实例变量的值
id object_getIvar ( id obj, Ivar ivar );
// 设置对象中实例变量的值
void object_setIvar ( id obj, Ivar ivar, id value );

对对象类操作

// 返回给定对象的类名
const char * object_getClassName ( id obj );
// 返回对象的类
Class object_getClass ( id obj );
// 设置对象的类
Class object_setClass ( id obj, Class cls );

获取类定义

// 获取已注冊的类定义的列表
int objc_getClassList ( Class *buffer, int bufferCount ); // 创建并返回一个指向全部已注冊类的指针列表
Class * objc_copyClassList ( unsigned int *outCount ); // 返回指定类的类定义
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name ); // 返回指定类的元类
Class objc_getMetaClass ( const char *name );

演示怎样使用

int numClasses;
Class * classes = NULL;
numClasses = objc_getClassList(NULL, 0); if (numClasses > 0) {
classes = malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
NSLog(@"number of classes: %d", numClasses); for (int i = 0; i < numClasses; i++) {
Class cls = classes[i];
NSLog(@"class name: %s", class_getName(cls));
}
free(classes);
}

设置关联值

Example : 在category 中加入对象

//.h
#import <UIKit/UIKit.h>
#import <objc/runtime.h> @interface UIView (AssociatedObject) @property (nonatomic, strong) id associatedObject; @end //.m
#import "UIView+AssociatedObject.h" @implementation UIView (AssociatedObject) static char kAssociatedObjectKey; - (void)setAssociatedObject:(id)associatedObject {
objc_setAssociatedObject(self, &kAssociatedObjectKey, associatedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} - (id)associatedObject {
return objc_getAssociatedObject(self, &kAssociatedObjectKey);
}

objc_setAssociatedObject,给指定的对象设置关联值。

objc_setAssociatedObject

Sets an associated value for a given object using a given key and association policy.

Declaration

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

Parameters

object

The source object for the association.

key

The key for the association.

value

The value to associate with the key key for object. Pass nil to clear an existing association.

policy

The policy for the association. For possible values, see Associative Object Behaviors.

  • object 指定的对象
  • const void *key key
  • value 值
  • policy 存储策略
Behavior @property Equivalent Description
OBJC_ASSOCIATION_ASSIGN @property (assign) 或 @property (unsafe_unretained) 指定一个关联对象的弱引用。
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) 指定一个关联对象的强引用,不能被原子化使用。

OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) 指定一个关联对象的copy引用。不能被原子化使用。
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) 指定一个关联对象的强引用,能被原子化使用。
OBJC_ASSOCIATION_COPY @property (atomic, copy) 指定一个关联对象的copy引用,能被原子化使用。

objc_getAssociatedObject

Returns the value associated with a given object for a given key.

Declaration

id objc_getAssociatedObject(id object, const void *key);

Parameters

object

The source object for the association.

key

The key for the association.

Return Value

The value associated with the key key for object.

objc_getAssociatedObject

返回给定对象的key的关联值

- object 关联的源对象

- key 关联的key

- Return Value 与对象的key相关联的值。

objc_removeAssociatedObjects

objc_removeAssociatedObjects

Removes all associations for a given object.

Declaration

void objc_removeAssociatedObjects(id object);

Parameters

object

An object that maintains associated objects.

Discussion

The main purpose of this function is to make it easy to return an object to a “pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use objc_setAssociatedObject with a nil value to clear an association.

删除给定对象的全部关联。

- object 对象(关联了很多值)

- 这个函数的主要目的是使对象返回一个“原始状态”,你不应该使用这个函数从对象中删除关联,由于它也删除了其它客户端可能加入到对象的关联 。

通常应该使用带有nil值的objc_setAssociatedObject来清除关联。

优秀样例

  • 加入私有属性用于更好地去实现细节。当扩展一个内建类的行为时,保持附加属性的状态可能非常必要。

    注意下面说的是一种非常教科书式的关联对象的用例:AFNetworking在 UIImageView 的category上用了关联对象来保持一个operation对象,用于从网络上某URL异步地获取一张图片。

  • 加入public属性来增强category的功能。

    有些情况下这样的(通过关联对象)让category行为更灵活的做法比在用一个带变量的方法来实现更有意义。

    在这些情况下,能够用关联对象实现一个一个对外开放的属性。回到上个AFNetworking的样例中的 UIImageView category。它的 imageResponseSerializer方法同意图片通过一个滤镜来显示、或在缓存到硬盘之前改变图片的内容。

  • 创建一个用于KVO的关联观察者。

    当在一个category的实现中使用KVO时,建议用一个自己定义的关联对象而不是该对象本身作观察者。ng an associated observer for KVO**. When using KVO in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself.

反例

  • 当值不须要的时候建立一个关联对象。一个常见的样例就是在view上创建一个方便的方法去保存来自model的属性、值或者其它混合的数据。假设那个数据在之后根本用不到。那么这样的方法尽管是没什么问题的。但用关联到对象的做法并不可取。

  • 当一个值能够被其它值推算出时建立一个关联对象。

    比如:在调用 cellForRowAtIndexPath: 时存储一个指向view的 UITableViewCell 中accessory view的引用。用于在 tableView:accessoryButtonTappedForRowWithIndexPath: 中使用。

  • 用关联对象替代X。这里的X能够代表下列含义:

    1. 当继承比扩展原有的类更方便时用子类化。
    2. 为事件的响应者加入响应动作。
    3. 当响应动作不方便使用时使用的手势动作捕捉。
    4. 行为能够在其它对象中被代理实现时要用代理(delegate)。

    5. 用NSNotification 和 NSNotificationCenter进行松耦合化的跨系统的事件通知。

动态加入方法

Example:

- (IBAction)addMethod:(id)sender {
[self addMethodForPerson];
if ([self.xjy respondsToSelector:@selector(speakMyName)]) {
[self.xjy performSelector:@selector(speakMyName)];
} else {
NSLog(@"未加入成功");
}
} - (void)addMethodForPerson {
class_addMethod([self.xjy class], @selector(speakMyName), (IMP)speakMyName, "v@:*");
} void speakMyName(id self,SEL _cmd) {
NSLog(@"加入成功啊QAQ");
}

class_addMethod

class_addMethod

Adds a new method to a class with a given name and implementation.

Declaration

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);

Parameters

cls

The class to which to add a method.

name

A selector that specifies the name of the method being added.

imp

A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.

types

An array of characters that describe the types of the arguments to the method. For possible values, see Objective-C Runtime Programming Guide > Type Encodings. Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).

Return Value

YES if the method was added successfully, otherwise NO (for example, the class already contains a method implementation with that name).

给一个类加入方法

- cls 被加入方法的类

- name 加入的方法的名称的SEL

- imp 方法的实现。该函数必须至少要有两个參数。self,_cmd.

class_addMethod加入实现将覆盖父类的实现,但不会替换此类中的现有实现。 要更改现有实现,请使用method_setImplementation。

Objective-C方法仅仅是一个C函数。至少须要两个參数 - self和_cmd。

比如,给定下面函数:

void myMethodIMP(id self,SEL _cmd)
{
     // implementation ....
}}

你能够动态地将它加入到类作为一个方法(称为resolveThisMethodDynamically)像这样:

class_addMethod([self class],@selector(resolveThisMethodDynamically),(IMP)myMethodIMP,“v @:”);

类型编码

Type Encodings

To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector.

为了辅助执行时系统,编译器对字符串中每一个方法的返回和參数类型进行编码,并将字符串与方法选择器相关联。

它使用的编码方案在其它上下文中也非常实用。因此能够通过@encode()编译器指令公开获得。

当给定类型规范时。@encode()返回该类型的字符串编码。 类型能够是基本类型,比如int。指针,标记结构或联合,或类名 - 实际上能够用作C sizeof()运算符的參数的不论什么类型。

详细内容參见 Objective-C Runtime Programming Guide

动态交换方法实现

Example:

#import "UIViewController+LogTracking.h"
#import <objc/runtime.h> @implementation UIViewController (LogTracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xjy_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector); //judge the method named swizzledMethod is already existed.
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// if swizzledMethod is already existed.
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
} - (void)xjy_viewWillAppear:(BOOL)animated {
[self xjy_viewWillAppear:animated];
NSLog(@"viewWillAppear : %@",self);
}
@end

+load vs +initialize

swizzling应该仅仅在+load中完毕。

在 Objective-C 的执行时中,每一个类有两个方法都会自己主动调用。+load 是在一个类被初始装载时调用,+initialize 是在应用第一次调用该类的类方法或实例方法前调用的。

两个方法都是可选的。而且仅仅有在方法被实现的情况下才会被调用。

dispatch_once

swizzling 应该仅仅在 dispatch_once 中完毕

由于 swizzling 改变了全局的状态。所以我们须要确保每一个预防措施在执行时都是可用的。

原子操作就是这样一个用于确保代码仅仅会被执行一次的预防措施,就算是在不同的线程中也能确保代码仅仅执行一次。Grand Central Dispatch 的 dispatch_once 满足了所须要的需求。而且应该被当做使用 swizzling 的初始化单例方法的标准。

method_getImplementation

method_getImplementation

Returns the implementation of a method.

Declaration

IMP method_getImplementation(Method m);

Parameters

method

The method to inspect.

Return Value

A function pointer of type IMP.

返回方法的实现

- method Method

method_getTypeEncoding

method_getTypeEncoding

Returns a string describing a method’s parameter and return types.

Declaration

const char * method_getTypeEncoding(Method m);

Parameters

method

The method to inspect.

Return Value

A C string. The string may be NULL.

返回一个C 字符串,描写叙述方法的參数和返回类型.

- method Method

class_replaceMethod

class_replaceMethod

Replaces the implementation of a method for a given class.

Declaration

IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);

Parameters

cls

The class you want to modify.

name

A selector that identifies the method whose implementation you want to replace.

imp

The new implementation for the method identified by name for the class identified by cls.

types

An array of characters that describe the types of the arguments to the method. For possible values, see Objective-C Runtime Programming Guide > Type Encodings. Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).

Return Value

The previous implementation of the method identified by name for the class identified by cls.

替换指定方法的实现

- cls class

- name selector

- imp 新的IMP

- types 类型编码

此函数以两种不同的方式执行:

1. 假设通过名称标识的方法不存在,则会像调用class_addMethod一样加入它。 由类型指定的类型编码按给定使用。

2. 假设按名称标识的方法存在,那么将替换其IMP,就好像调用了method_setImplementation。

将忽略由types指定的类型编码。

method_exchangeImplementations

method_exchangeImplementations

Exchanges the implementations of two methods.

Declaration

void method_exchangeImplementations(Method m1, Method m2);

交换两个方法的实现.

原子版本号的实现:

IMP imp1 = method_getImplementation(m1);
IMP imp2 = method_getImplementation(m2);
method_setImplementation(m1, imp2);
method_setImplementation(m2, imp1);

Selectors, Methods, & Implementations

在 Objective-C 的执行时中,selectors, methods, implementations 指代了不同概念。然而我们一般会说在消息发送过程中,这三个概念是能够相互转换的。 下面是苹果 Objective-C Runtime Reference中的描写叙述:

  • Selector(typedef struct objc_selector *SEL):在执行时 Selectors 用来代表一个方法的名字。Selector 是一个在执行时被注冊(或映射)的C类型字符串。Selector由编译器产生而且在当类被载入进内存时由执行时自己主动进行名字和实现的映射。
  • Method(typedef struct objc_method *Method):方法是一个不透明的用来代表一个方法的定义的类型。

  • Implementation(typedef id (*IMP)(id, SEL,…)):这个数据类型指向一个方法的实现的最開始的地方。该方法为当前CPU架构使用标准的C方法调用来实现。该方法的第一个參数指向调用方法的自身(即内存中类的实例对象,若是调用类方法,该指针则是指向元类对象metaclass)。第二个參数是这种方法的名字selector,该方法的真正參数紧随其后。

理解 selector, method, implementation 这三个概念之间关系的最好方式是:在执行时。类(Class)维护了一个消息分发列表来解决消息的正确发送。每一个消息列表的入口是一个方法(Method),这种方法映射了一对键值对,当中键是这种方法的名字 selector(SEL),值是指向这种方法实现的函数指针 implementation(IMP)。

Method swizzling 改动了类的消息分发列表使得已经存在的 selector 映射了还有一个实现 implementation,同一时候重命名了原生方法的实现为一个新的 selector。

思考

非常多人觉得交换方法实现会带来无法预料的结果。

然而採取了下面预防措施后, method swizzling 会变得非常可靠:

  • 在交换方法实现后记得要调用原生方法的实现(除非你非常确定能够不用调用原生方法的实现):APIs 提供了输入输出的规则,而在输入输出中间的方法实现就是一个看不见的黑盒。交换了方法实现而且一些回调方法不会调用原生方法的实现这可能会造成底层实现的崩溃。
  • 避免冲突:为分类的方法加前缀,一定要确保调用了原生方法的全部地方不会由于你交换了方法的实现而出现意想不到的结果。
  • 理解实现原理:仅仅是简单的拷贝粘贴交换方法实现的代码而不去理解实现原理不仅会让 App 非常脆弱,而且浪费了学习 Objective-C 执行时的机会。

    阅读 Objective-C Runtime Reference 而且浏览 能够让你更好理解实现原理。

  • 持续的预防:无论你对你理解 swlzzling 框架,UIKit 或者其它内嵌框架有多自信。一定要记住全部东西在下一个发行版本号都可能变得不再好使。

    做好准备,在使用这个黑魔法中走得更远,不要让程序反而出现不可思议的行为。

iOS Objc Runtime 教程+实例Demo的更多相关文章

  1. iOS 开发-- Runtime 1小时入门教程

    1小时让你知道什么是Objective-C Runtime,并对它有一定的基本了解,可以在开发过程中运用自如. 三.Objective-C Runtime到底是什么东西? 简而言之,Objective ...

  2. 使用objc runtime实现iOS绿色的懒加载

    使用objc runtime实现懒加载 地址:AutoPropertyCocoa 本文所指懒加载形式如下 - (id)lazyloadProperty{ if(_lazyloadProperty == ...

  3. ios专题 - objc runtime 动态增加属性

    objective-c中,有类别可以在不修改源码的基础上增加方法:近排在看别人的开源代码时,发现还可以动态增加属性.而且是在运行时,太牛B了. 使用运行时库,必须要先引入 objc/runtime.h ...

  4. iOS - OC RunTime 运行时

    1.运行时的使用 向分类中添加属性 // 包含运行时头文件 #import <objc/runtime.h> /* void objc_setAssociatedObject(id obj ...

  5. iOS之RunTime浅谈

    首先说一下什么是runtime:RunTime简称运行时.就是系统在运行的时候的一些机制,其中最主要的是消息机制.对于C语言,函数的调用 在编译的时候会决定调用哪个函数( C语言的函数调用请看这里 ) ...

  6. 关于iOS的runtime

    runtime是一个很有意思的东西,如果你学iOS开发很经常就会用到或被问到runtime.那么runtime是什么呢,如何去了解它. runtime:中文名 运行时,系统在编译时留下的一些 类型,操 ...

  7. iOS:runtime最全的知识总结

    runtime 完整总结 好东西,应该拿出来与大家分享... 南峰子博客地址:http://southpeak.github.io/blog/categories/ios/ 原文链接:http://w ...

  8. iOS开发-Runtime详解(简书)

    简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [receiver message]; // ...

  9. IOS的属性和实例变量

    实际上,@property声明的是属性,并不是实例变量.但是编译器会根据属性,自动生成实例变量,和对应的access方法.所以已经在interface里声明了@property,就不再需要在imple ...

随机推荐

  1. go channel实现

    go channel实现 Go语言经过多年的发展,于最近推出了第一个稳定版本.相对于C/C++来说,Go有很多独特之出,比如提供了相当抽象的工具,如channel和goroutine.本文主要介绍ch ...

  2. 我的第一个Django项目

    1.创建Django项目 命令:django-admin startproject 项目名 注意:创建应用必须先进入虚拟环境. 项目目录如下: 目录层级说明: __init__.py: 说明demo0 ...

  3. shutdown---系统关机

    shutdown命令用来系统关机命令.shutdown指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作. 语法 shutdown(选项)(参数) 选项 -c:当执行“shutdown - ...

  4. 00079_增强for循环

    1.格式 /* * JDK1.5新特性,增强for循环 * JDK1.5版本后,出现新的接口 java.lang.Iterable * Collection开始继承Iterable * Iterabl ...

  5. 【习题 8-12 UVA - 1153】Keep the Customer Satisfied

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 结束时间比较早的,就早点开始做. 所以,将n件事情,按照结束时间升序排. 然后对于第i件事情. 尽量把它往左排. 即t+1..t+a ...

  6. ubuntu mysql 无法启动 简单排查

    自己的站点非常久没有去上了,想打开发现竟然打不开了.所以就找了一系列的原因. vps不行了 dns解析出问题了 域名出问题了 简单排查之后,我的vps服务商用的是 ***(bandwagonhost) ...

  7. Mahout项目开发环境搭建(Eclipse\MyEclipse + Maven)

    继续 http://www.tuicool.com/articles/rmiEz2 http://www.cnblogs.com/jchubby/p/4454888.html

  8. 洛谷P3273 [SCOI2011]棘手的操作

    题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权 ...

  9. position(static-relative-absolute-fixed),margin(top-right-bottom-left),top-right-bottom-left

    最近写css遇到一些问题,所以准备写下来捋一下思路. 1.position=satic下看margin的使用.(top-right-bottom-left在这种case下无效) 1-1)margin ...

  10. python学习一,python基本语法

    python基本语法: 1.python基本语句结构: 首先,在其他的语言中,比如java,c++,c#等,没写完一行语句之后,都需要在语句的末尾加一个分号,表示该语句结束,但是在python中,我们 ...