本篇文章在《iOS開發(fā)之Runtime常用示例總結(jié)》基礎(chǔ)上修改,地址是
「:」http://www.cocoachina.com/ios/20170301/18804.html
本篇文章主要創(chuàng)建的類如下:
首先我們先創(chuàng)建一個實例類TestClass
這個類實現(xiàn)了NSCopying和NSCoding協(xié)議,包含公共的成員屬性,和實例方法以及類方法,
在.m文件中
我們有私有成員變量和成員屬性选侨,和私有方法鱼鼓。
下面我們來具體介紹一下runtime的常用方法
1钉跷、利用runtime來獲取類名
/**
獲取類名
@param class 相應類
@return NSString:類名
*/
+ (NSString *)fetchClassName:(Class)class
{
const char *className = class_getName(class);
return [NSString stringWithUTF8String:className];
}
2灶伊、利用runtime來獲取成員變量
/**
獲取成員變量
@param class Class
@return NSArray
*/
+(NSArray *)fetchIvarList:(Class)class
{
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(class, &count);//獲取成員變量的個數(shù)count疆前,以及數(shù)組內(nèi)容
NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i ++) {
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
const char *ivarName = ivar_getName(ivarList[i]);//獲取成員變量的名稱
const char *ivarType = ivar_getTypeEncoding(ivarList[i]);//獲取成員變量的類型
dic[@"type"] = [NSString stringWithUTF8String:ivarType];
dic[@"ivarName"] = [NSString stringWithUTF8String:ivarName];
[mutableList addObject:dic];
}
free(ivarList);
return [NSArray arrayWithArray:mutableList];
}
其輸出結(jié)果如下:
- 注意,如果用這個來獲取類的成員變量聘萨,是不分私有和公有之分的竹椒,都會獲取出來,意思也就是不管是成員變量米辐,還是成員屬性胸完,都可以獲取的到,并且獲取到的成員屬性的名稱是有下劃線的
3翘贮、獲取類的成員屬性
- 注意,如果用這個來獲取類的成員變量聘萨,是不分私有和公有之分的竹椒,都會獲取出來,意思也就是不管是成員變量米辐,還是成員屬性胸完,都可以獲取的到,并且獲取到的成員屬性的名稱是有下劃線的
/**
獲取類的屬性列表赊窥,包括私有和公有屬性,以及定義再延展中的屬性
@param class Class
@return 屬性列表數(shù)組
*/
+(NSArray *)fetchPropertyList:(Class)class
{
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList(class, &count);
NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i ++) {
const char *propertyName = property_getName(propertyList[i]);
[mutableList addObject:[NSString stringWithUTF8String:propertyName]];
}
free(propertyList);
return mutableList;
}
其輸出結(jié)果如下:
- 注意狸页,獲取的成員屬性名稱是沒有下劃線的锨能,此處也不分私有和公有
4、獲取類的實例方法列表
- 注意狸页,獲取的成員屬性名稱是沒有下劃線的锨能,此處也不分私有和公有
/**
獲取類的實例方法列表:getter芍耘,setter址遇,對象方法等。但不能獲取類方法斋竞,也就是加號方法
@param class class
@return 類的實例方法列表
*/
+ (NSArray *)fetchMethodList:(Class)class
{
unsigned int count = 0;
Method *methodList = class_copyMethodList(class, &count);
NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0 ; i < count; i ++) {
Method method = methodList[i];
SEL methodName = method_getName(method);
[mutableList addObject:NSStringFromSelector(methodName)];
}
free(methodList);
return mutableList;
}
其輸出結(jié)果如下:
- 注意傲隶,其中類的成員屬性的getter,setter方法也獲取不到的窃页,但是類方法是獲取不到
5跺株、獲取類的協(xié)議列表
- 注意傲隶,其中類的成員屬性的getter,setter方法也獲取不到的窃页,但是類方法是獲取不到
/**
獲取協(xié)議列表
@param class class
@return 協(xié)議列表
*/
+(NSArray *)fetchProtocolList:(Class)class
{
unsigned int count = 0;
__unsafe_unretained Protocol **protocolList = class_copyProtocolList(class, &count);
NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i ++) {
Protocol *protocol = protocolList[i];
const char *protocolName = protocol_getName(protocol);
[mutableList addObject:[NSString stringWithUTF8String:protocolName]];
}
return mutableList;
}
其輸出結(jié)果是
6、實現(xiàn)方法的交換
/** 方法交換 @param class 交換方法所在的類 @param method1 方法1 @param method2 方法2 */ + (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2 { Method firstMethod = class_getInstanceMethod(class, method1); Method secondMethod = class_getInstanceMethod(class, method2); method_exchangeImplementations(firstMethod, secondMethod); }
例如我們在TestClass(SwapMethod)類別中脖卖,寫如下代碼:
#import "TestClass+SwapMethod.h"
#import "Runtime.h"
@implementation TestClass(SwapMethod)
+ (void)load
{
[Runtime methodSwap:[self class] firstMethod:@selector(method1) secondMethod:@selector(method2)];
}
- (void)method2
{
NSLog(@"這里實際上調(diào)用的是method1的實現(xiàn)");
}
@end
然后我們?nèi)フ{(diào)用method2乒省,會發(fā)現(xiàn)調(diào)用的方法實際上是method1的方法,調(diào)用method1方法畦木,實際上調(diào)用的是method2的方法
7袖扛、給類添加新的方法與實現(xiàn)
/**
給類上添加新的方法與實現(xiàn)
@param class 相應的類
@param methodSel 方法的名
@param methodSelImpl 對應方法實現(xiàn)的方法名
*/
+ (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl
{
Method method = class_getInstanceMethod(class, methodSelImpl);
IMP methodIMP = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
class_addMethod(class, methodSel, methodIMP, types);
}
- *注意,此方法添加的是實例方法十籍,而非類方法
當我們訪問一個方法時蛆封,如果找不到此方法的實現(xiàn),iOS提供了兩個方法去訪問勾栗,
+ (BOOL)resolveClassMethod:(SEL)sel;//針對訪問不到類方法時惨篱,訪問的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel;//針對訪問不到實例方法時,訪問的方法
我們以實例方法未準围俘,假如我們在TestClass中添加一個方法砸讳,然后不去實現(xiàn)
此時琢融,如果訪問此方法,則系統(tǒng)會找不到此方法的實現(xiàn)簿寂,此時我們重寫resolveInstanceMethod方法漾抬,你會發(fā)現(xiàn)系統(tǒng)會訪問此方法,如果我們在此方法中給未實現(xiàn)的方法添加實現(xiàn)常遂,則系統(tǒng)會去訪問我們提供的實現(xiàn)方法的纳令,例如:
void addnewMethodIMP(id self,SEL _cmd,NSString *temp)
{
NSLog(@"動態(tài)添加實現(xiàn)");
}
- (void)addMethodComplete:(NSString *)value
{
NSLog(@"oc替換的方法:%@",value);
}
//此方法時針對對象方法的
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
#warning mark-- 以下是兩種添加形式
//第一種
//其中“v@:”代表的是針對誰添加的方法,如果后面帶有參數(shù)克胳,則寫在:后面泊碑,例如下面的 "v@:@"
// if (sel == @selector(missMethod:)) {
// class_addMethod([self class], sel, (IMP)addnewMethodIMP, "v@:@");
// return YES;
// }
//第二種 此種方法就是我們上面寫的
// [Runtime addMethod:[self class] method:sel method:@selector(addMethodComplete:)];
// return YES;
// return [super resolveInstanceMethod:sel];
return NO;
}
8、屬性關(guān)聯(lián)
在類目中動態(tài)的為我們的類添加相應的屬性毯欣。
下方就是在TestClass的類目中通過objc_getAssociatedObject()和objc_setAssociatedObject()兩個方法為TestClass類添加了一個addNewProperty屬性
#import "TestClass+AssociatedObject.h"
@interface TestClass (AssociatedObject)
@property (nonatomic,strong)NSString *addNewProperty;
@end
@implementation TestClass(AssociatedObject)
#pragma mark-- 動態(tài)屬性關(guān)聯(lián)
static char addNewProperty;
/**
getter方法
@return 返回關(guān)聯(lián)屬性的值
*/
- (NSString *)addNewProperty
{
return objc_getAssociatedObject(self, &addNewProperty);
}
/**
setter方法
@param addNewProperty 設(shè)置關(guān)聯(lián)屬性的值
*/
- (void)setAddNewProperty:(NSString *)addNewProperty
{
objc_setAssociatedObject(self, &addNewProperty, addNewProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
此時如果我們再去獲取TestClass的屬性,其輸出結(jié)果就是
其中包含了我們動態(tài)添加的addNewProperty屬性
9臭脓、下面我們用runtime來做一些數(shù)據(jù)解析的應用
首先我們創(chuàng)建一個NSObject(data)的類別酗钞,注意,運用runtime一定要導入頭文件#import <objc/runtime.h>
- 1 將對象轉(zhuǎn)化為字典
- (NSDictionary *)objectToDic
{
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
NSMutableDictionary *propertyDic = [NSMutableDictionary dictionaryWithCapacity:count];
for (unsigned int i = 0; i < count; i ++) {
objc_property_t property = propertyList[i];
NSString *key = [NSString stringWithUTF8String:property_getName(property)];
id value = [self valueForKey:key];
if (value == nil) {
value = [NSNull null];
}
else
{
value = [self getObjectInternal:value];
}
[propertyDic setObject:value forKey:key];
}
return propertyDic;
}
//此方法是針對来累,當如果我們的對象中含有其他對象時砚作,我們可以將其對象也轉(zhuǎn)換為字典的形式
- (id)getObjectInternal:(id)obj
{
if ([obj isKindOfClass:[NSString class]]||[obj isKindOfClass:[NSNumber class]]||[obj isKindOfClass:[NSNull class]]) {
return obj;
}
if ([obj isKindOfClass:[NSArray class]]) {
NSArray *objArr = obj;
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:objArr.count];
for (int i = 0 ; i < objArr.count; i ++) {
[arr setObject:[self getObjectInternal:[objArr objectAtIndex:i]] atIndexedSubscript:i];
}
return arr;
}
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *objDic = obj;
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:[objDic count]];
for (NSString *key in objDic.allKeys) {
[dic setValue:[self getObjectInternal:[objDic objectForKey:key]] forKey:key];
}
return dic;
}
return [obj objectToDic];
}
其中 - (id)getObjectInternal:(id)obj這個方法,是當我們的對象中含有其他對象時嘹锁,我們也可以將其轉(zhuǎn)換為字典的形式葫录,例如:
Person類中含有Cat類,
當我們用Person對象調(diào)用轉(zhuǎn)換為字典的方法時领猾,Person對象中含有的Cat對象也會被轉(zhuǎn)換為字典的形式米同,例如:
其輸出結(jié)果是:
然后我們將其轉(zhuǎn)化為json字符串
//將字典轉(zhuǎn)化為json
- (NSString *)dicToJson
{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString;
if (!jsonData) {
NSLog(@"%@",error);
}else{
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
NSRange range = {0,jsonString.length};
//去掉字符串中的空格
[mutStr replaceOccurrencesOfString:@" " withString:@"" options:NSLiteralSearch range:range];
NSRange range2 = {0,mutStr.length};
//去掉字符串中的換行符
[mutStr replaceOccurrencesOfString:@"\n" withString:@"" options:NSLiteralSearch range:range2];
return mutStr;
}
如果要將json轉(zhuǎn)化為字典則調(diào)用下面的方法
//json轉(zhuǎn)化為字典
- (NSDictionary *)jsonToDic
{
if (self == nil) {
return nil;
}
NSData *jsonData = [(NSString *)self dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
if (err) {
return nil;
}
return dic;
}
- 2 字典轉(zhuǎn)化為對象
/**
字典轉(zhuǎn)對象
@param dic 字典
@return 屬性已經(jīng)有值的對象實例
*/
- (id)dicToObject:(NSDictionary *)dic
{
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (unsigned int i = 0 ; i < count; i ++) {
objc_property_t property = propertyList[i];
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
NSString *propertyType = [NSString stringWithUTF8String:property_getAttributes(property)];
if ([[dic allKeys] containsObject:propertyName])
{
id value = [dic valueForKey:propertyName];
if (![value isKindOfClass:[NSNull class]]&&value!=nil) {
if ([value isKindOfClass:[NSDictionary class]]) {
id pro = [self cretateInstanceByClassName:[self getClassName:propertyType]];
[pro dicToObject:value];
[self setValue:pro forKey:propertyName];
}
else
{
[self setValue:value forKey:propertyName];
}
}
else
{
value = [NSNull null];
[self setValue:value forKey:propertyName];
}
}
}
return self;
}
//獲取屬性類型名字
- (NSString *)getClassName:(NSString *)typeString
{
NSArray * attributes = [typeString componentsSeparatedByString:@","];
// T@"NSDictionary",&,N,V_tempDic
if (attributes.count>0) {
NSString * typeAttribute = [attributes objectAtIndex:0];
if ([typeAttribute hasPrefix:@"T@"] && [typeAttribute length] > 1) {
NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)]; return typeClassName;
}
}
return typeString;
}
//下面的方法是我們針對當對象中含有某個類時,做的處理
//根據(jù)類名稱摔竿,創(chuàng)建一個實例對象
- (id)cretateInstanceByClassName:(NSString *)className
{
NSBundle *bundle = [NSBundle mainBundle];
Class aclass = [bundle classNamed:className];
id anInstance = [[aclass alloc] init];
return anInstance;
}
例如面粮,當我們用上面的對象轉(zhuǎn)字典輸出的字典,經(jīng)過這個字典轉(zhuǎn)對象的方法继低,
其輸出的結(jié)果是我們賦給的catName的名稱@“bb”
- 3 利用runtime對象序列化熬苍,當然我們要實現(xiàn)NSCoding協(xié)議
- (instancetype)initWithCoder:(NSCoder *)decoder
{
if (self = [super init]) {
unsigned int count = 0;
//獲取類中所有成員變量名
Ivar *ivar = class_copyIvarList([self class], &count);
for (int i = 0; i<count; i++) {
Ivar iva = ivar[i];
const char *name = ivar_getName(iva);
NSString *strName = [NSString stringWithUTF8String:name];
//進行解檔取值
id value = [decoder decodeObjectForKey:strName];
//利用KVC對屬性賦值
[self setValue:value forKey:strName];
}
free(ivar);
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
unsigned int count;
Ivar *ivar = class_copyIvarList([self class], &count);
for (int i=0; i<count; i++) {
Ivar iv = ivar[i];
const char *name = ivar_getName(iv);
NSString *strName = [NSString stringWithUTF8String:name];
//利用KVC取值
id value = [self valueForKey:strName];
[encoder encodeObject:value forKey:strName];
}
free(ivar);
}
- 4 利用runtime實現(xiàn)UIAlertView的Block回調(diào)
平時我們用UIAlertView需要使用其代理方法來確定我們的點擊事件,使用起來不夠方便袁翁,新的sdk中UIAlertViewController是使用block來訪問其點擊事件的柴底,那我們就將UIAlertView也封裝成可以利用block來訪問點擊事件的類別
首先我們需要一個block屬性值
@interface UIAlertView () <UIAlertViewDelegate>
@property (copy, nonatomic) void (^block)(UIAlertView *UIAlertView, NSInteger buttonIndex);
@end
利用數(shù)組來添加按鈕
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSArray *)otherButtonTitles
{
self = [self initWithTitle:title message:message
delegate:nil
cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];
if (self) {
for (NSString *otherButtonTitle in otherButtonTitles) {
[self addButtonWithTitle:otherButtonTitle];
}
}
return self;
}
將alertView與block關(guān)聯(lián)起來(通過runtime)注意:要導入頭文件
import <objc/runtime.h>
這里也就是動態(tài)給alertView添加了block屬性
- (void)setBlock:(void (^)(UIAlertView *, NSInteger))block
{
objc_setAssociatedObject(self, @selector(block), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void (^)(UIAlertView *, NSInteger))block
{
return objc_getAssociatedObject(self, @selector(block));
}
當點擊alertview 的按鈕時
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (self.block) {
self.block(alertView, buttonIndex);
}
}
下面的方法就是block回調(diào)
- (void)showUsingBlock:(void (^)(UIAlertView *, NSInteger))block
{
self.delegate = self;
self.block = block;
[self show];
}
通過調(diào)用此方法,得到的block回調(diào)值來判斷當前點擊的按鈕
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"" delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil];
[alert showUsingBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
}];
當我們需要針對ios8之前和之后的版本做適配時粱胜,我們可以寫出一個公共的方法來調(diào)用alertView柄驻,代碼如下:
+ (void)alertTitle:(NSString *)title message:(NSString *)message cancelButtonTitle: (NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles showInVC: (UIViewController *)vc index:(void (^)(NSInteger))block
{
if (IsIOS8Early) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:title message:message cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles];
[alert showUsingBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
block(buttonIndex);
}];
}
else
{
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
block(0);
}];
[alertVC addAction:cancelAction];
if (otherButtonTitles&&otherButtonTitles.count>0) {
for (int i = 0; i < otherButtonTitles.count; i ++)
{
UIAlertAction *okAction = [UIAlertAction actionWithTitle:otherButtonTitles[i] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
block(i +1);
}];
[alertVC addAction:okAction];
}
}
[vc presentViewController:alertVC animated:YES completion:nil];
}
}
如果我們需要對UIActionSheet做出這樣的適配,也可以用上述方法來解決
以上就是runtime的常用示例的介紹焙压,此文轉(zhuǎn)自
「:」http://www.cocoachina.com/ios/20170301/18804.html
若有不理解的凿歼,請?zhí)D(zhuǎn)至此鏈接