#import <Foundation/Foundation.h>
@interface CYPerson : NSObject
{
int _money;
}
@property (nonatomic, assign, readonly) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSArray *books;
@property (nonatomic, strong) id test;
@property (nonatomic, assign) CGRect rect;
@property (nonatomic, copy) void (^block)();
@property (nonatomic, assign) int *p;
@property (nonatomic, strong) CYCat *cat;
@end
#import "CYPerson.h"
@implementation CYPerson
@end
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "CYPerson.h"
void getIvars()
{
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([CYPerson class], &count);
// class_copyIvarList獲取成員變量列表,它返回的是一個(gè)指針指向的是最前面的那一個(gè)
// ([CYPerson class], &count)這里面放的是類,你把類給我,我就獲取里面所有的成員變量。&count放的是成員變量的個(gè)數(shù)
for (int i = 0; i < count; i++) {
// for循環(huán)把所有成員變量遍歷出來(lái)
Ivar ivar = ivars[i]; // *(ivars + i)
// 取出每一個(gè)成員變量Ivar,而且Ivar是以數(shù)組的形式去訪問(wèn)
// ivars[i]的寫法相當(dāng)于*(ivars + i)---跳到下一個(gè),取出這個(gè)指針指向的東西
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
// %s是因?yàn)檫@里是C語(yǔ)言的東西
// ivar_getName(ivar)這是去獲取所有成員變量的名字,這樣就挖掘到了所有成員變量
// ivar_getTypeEncoding(ivar)表示獲取一個(gè)成員變量的類型字符串
// 而且今后如果你有要獲取所有什么double,int類型的成員變量,這種方式,一打印就出來(lái)了
}
free(ivars);
// 這里是通過(guò)copy出來(lái)的,所以需要釋放的
}
// 這里獲取所有屬性,和上面一樣
int main(int argc, const char * argv[]) {
@autoreleasepool {
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([CYPerson class], &count);
// class_copyPropertyList獲取屬性列表
// class_copyProtocolList([CYPerson class], &count);是獲取協(xié)議列表,這樣可以獲取這個(gè)類遵循的所有協(xié)議
// class_copyMethodList([CYPerson class], &count);是獲取方法列表,這樣可以知道這個(gè)類遵循 那些方法
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSLog(@"%s ---- %s", property_getName(property), property_getAttributes(property));
// property_getName(property)這是去獲取所有屬性的名字
// property_getAttributes(property)獲取所有屬性的類型
}
free(properties);
// method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
// 方法交換---這個(gè)方法的意義在于我可以將蘋果系統(tǒng)內(nèi)部的方法替換成我自己的方法。當(dāng)你認(rèn)為系統(tǒng)自帶的方法不好用時(shí),你就將它替換為自己的方法
}
return 0;
}
*
*
*
*
*
*
- 運(yùn)行時(shí)還可以動(dòng)態(tài)添加成員變量方法,動(dòng)態(tài)添加方法,動(dòng)態(tài)添加屬性,動(dòng)態(tài)添加協(xié)議這四個(gè)方法
- 假如說(shuō)在代碼里面你只寫了如上幾個(gè)屬性,但是在我動(dòng)態(tài)添加后,在程序運(yùn)行過(guò)程中會(huì)無(wú)緣無(wú)故多一個(gè)屬性出來(lái),但是你在代碼中是看不見的。這就是運(yùn)行時(shí)能干的事情,相當(dāng)于操縱你的內(nèi)存,但是這也僅僅是運(yùn)行時(shí)的冰山一角
*
*
method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
// 方法交換---這個(gè)方法的意義在于我可以將蘋果系統(tǒng)內(nèi)部的方法替換成我自己的方法。當(dāng)你認(rèn)為系統(tǒng)自帶的方法不好用時(shí),你就將它替換為自己的方法
-
RunTime
- 1.獲取內(nèi)部所有屬性和成員變量
- 2.Method Swizzle--iOS黑魔法
- 在CYPerson.h文件中
#import <Foundation/Foundation.h>
@interface CYPerson : NSObject
- (void)run;
- (void)study;
@end
#import "CYPerson.h"
@implementation CYPerson
- (void)run
{
NSLog(@"%s", __func__);
}
- (void)study
{
NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "CYPerson.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Method method1 = class_getInstanceMethod([CYPerson class], @selector(run));
Method method2 = class_getInstanceMethod([CYPerson class], @selector(study));
method_exchangeImplementations(method1, method2);
CYPerson *p = [[CYPerson alloc] init];
[p run];
[p study];
- 可以看出來(lái):我調(diào)用run,它執(zhí)行的study。我調(diào)用study,它執(zhí)行的run
- 那這個(gè)有啥用呢?
- 在這里看出來(lái)是沒(méi)什么卵用,但是它的價(jià)值就在于可以交換系統(tǒng)自帶的方法
- 比如說(shuō)我可以讓控制器的viewDidLoad方法換成我自己的viewDidLoad方法,讓它執(zhí)行的時(shí)候執(zhí)行到我自己的方法中去了。
- 下面舉個(gè)例子:
- 我想監(jiān)控控制器什么時(shí)候死亡,那我就得重寫dealloc方法。那么假如我們的項(xiàng)目中有30個(gè)或者更多的控制器,我想看它死掉沒(méi)有,防止循環(huán)引用,那我是不是20個(gè)都得實(shí)現(xiàn)dealloc方法?那我何不再搞一個(gè)方法出來(lái),比如說(shuō)CY_dealloc:方法。你本來(lái)想執(zhí)行dealloc的,現(xiàn)在執(zhí)行到我CY_dealloc:方法里去。那我就可以在這里統(tǒng)一監(jiān)控所有控制器的死亡
- 下面我就做一下這個(gè)事情。為了保證所有控制器都有,我弄一個(gè)控制器的分類--UIViewController+CYExtention
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@implementation UIViewController (CYExtension)
+ (void)load
{
Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
Method method2 = class_getInstanceMethod(self, @selector(CY_dealloc));
method_exchangeImplementations(method1, method2);
}
- (void)CY_dealloc
{
NSLog(@"%@ - CY_dealloc", self);
}
@end
- 這樣就讓所有控制器的dealloc來(lái)到了我CY_dealloc里面,我可以統(tǒng)一監(jiān)控所有控制器
- 上面我沒(méi)有導(dǎo)入頭文件,是因?yàn)閘oad方法是只要載入到內(nèi)存就會(huì)調(diào)一次,這個(gè)分類文件肯定是要裝入內(nèi)存的,所以頭文件要不要都無(wú)所謂,沒(méi)什么價(jià)值。但是你的導(dǎo)入#import <UIKit/UIKit.h>。因?yàn)槲业媚每刂破鞯拿?/li>
- 我在Main.storyboard中弄多一些控制器會(huì)發(fā)現(xiàn):
- 這樣的話,任何一個(gè)控制器掛了我都能監(jiān)控得到
- 這里有一個(gè)小問(wèn)題:
- 我在viewController.m文件中加上dealloc
- (void)dealloc
{
NSLog(@"-------dealloc");
}
- 然后讓上圖中的第二個(gè)控制器class為我們的viewController,運(yùn)行打印,會(huì)發(fā)現(xiàn)
- 按理來(lái)說(shuō),一運(yùn)行點(diǎn)擊button后點(diǎn)擊返回是應(yīng)該調(diào)用dealloc方法,但是一調(diào)用它是應(yīng)該來(lái)到我們的CY_dealloc方法,應(yīng)該是只打印出CY_dealloc的,但是打印出來(lái)還是調(diào)用了dealloc方法。這是為什么呢?
- 你會(huì)發(fā)現(xiàn),我們開始寫Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));的時(shí)候,開始如果寫的是@selector(@"dealloc"),這是禁止的,說(shuō)明這個(gè)方法ARC情況下是比較特殊的。但是前面@selector(run)和@selector(study)它們交換方法后,只打印了對(duì)方的方法,自己的方法就不會(huì)調(diào)用了。
- 但是這里這么弄也正是我要說(shuō)的效果:用我們的方法交換系統(tǒng)的方法后還是想辦到系統(tǒng)的方法還是能調(diào)。因?yàn)槲覀冇袝r(shí)候是想在控制器死掉的時(shí)候做一些事情。比如說(shuō):在控制器死掉的時(shí)候清?掉一些東西,或者在控制器死掉的時(shí)候做一些請(qǐng)求:取消通知
- (void)dealloc
{
NSLog(@"-------dealloc");
self.data = nil;
self.images = nil;
[self cancel];
}
- 但是如果按照開始的做法,我把dealloc換成CY_dealloc,就意味著我以后調(diào)dealloc只會(huì)來(lái)到CY_dealloc,以前系統(tǒng)的dealloc就沒(méi)法執(zhí)行。但是這里上面系統(tǒng)的dealloc可以執(zhí)行,但我告訴你如果換成了普通的方法它是不可能執(zhí)行的。就行前面我執(zhí)行run的時(shí)候它不可能執(zhí)行study,這兩個(gè)方法只會(huì)執(zhí)行它對(duì)應(yīng)的方法
- 所以今后你如果做了方法的調(diào)換,不是系統(tǒng)的,我說(shuō)的是普通的方法換了。你如果調(diào)換后還想調(diào)回以前的方法,那就做一件事情執(zhí)行以下:(self.對(duì)應(yīng)的方法)。只有這樣它才會(huì)又調(diào)回自己以前的方法。因?yàn)槟氵@樣僅僅是做一個(gè)攔截嘛!就像下面:
- (void)CY_dealloc
{
NSLog(@"%@ - CY_dealloc", self);
[self CY_dealloc];
// 這樣調(diào)用的是dealloc,又調(diào)回去了
}
- 這里的意思:當(dāng)一個(gè)控制器死掉的時(shí)候,它肯定會(huì)調(diào)dealloc,但dealloc方法實(shí)現(xiàn)被我換掉了。所以它來(lái)到了CY_dealloc。執(zhí)行完- (void)CY_dealloc
{
NSLog(@"%@ - CY_dealloc", self);這段代碼,我再調(diào)用[self CY_dealloc];,它的方法實(shí)現(xiàn)是dealloc,所以又調(diào)回去了。所以今后你把系統(tǒng)方法換掉以后,我建議你再調(diào)回去,當(dāng)然有些特殊情況就特殊處理。
- 那這個(gè)有什么價(jià)值呢?
- 第一個(gè)以及體現(xiàn)了價(jià)值:攔截系統(tǒng)方法調(diào)用,我就可以在系統(tǒng)方法調(diào)用之前或者調(diào)用之后做一些事情
- 第二個(gè)價(jià)值舉個(gè)例子:
- 使用字典和數(shù)組的時(shí)候一直有一個(gè)頭疼的問(wèn)題:字典和數(shù)組賦值的時(shí)候不能為空,否則報(bào)錯(cuò): object cannot be nil'
NSString *value = nil;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
// dict[@"name"] = value;
NSMutableArray *array = [NSMutableArray array];
[array addObject:value];
array[0] = value;
- 但是我們很多時(shí)候不知道別人給你傳的值是不是空啊!為了不報(bào)錯(cuò),我們經(jīng)常都得進(jìn)行判斷:如果value有值,我再執(zhí)行。
- 那有沒(méi)有一個(gè)辦法可以過(guò)濾掉這些東西呢?只要發(fā)現(xiàn)傳進(jìn)來(lái)的值是個(gè)空的,我就不處理,那可不可以一勞永逸做到這點(diǎn)呢?我們只要寫下某一段代碼,以后凡是項(xiàng)目中傳空的值進(jìn)來(lái),它都不報(bào)錯(cuò)了,怎么去做呢?
- 只要將NSMutable--的addObject:方法換掉,攔截這個(gè)方法,鴨蛋發(fā)現(xiàn)傳進(jìn)來(lái)的值是空的我就不調(diào),如果傳進(jìn)來(lái)的值不是nil,我就調(diào)回它以前的方法。那么這樣的話今后數(shù)組的訪問(wèn)或者字典的訪問(wèn)就不會(huì)存在傳進(jìn)來(lái)值為空?qǐng)?bào)錯(cuò)的問(wèn)題了
- 還舉個(gè)例子:[UIImage imageName:我完全可以將imageName:方法攔截一下,我就可以對(duì)它傳進(jìn)來(lái)的圖片名進(jìn)行統(tǒng)一處理,比如說(shuō)什么夜間模式,什么皮膚,我就可以根據(jù)文件名去加載另外一個(gè)文件,統(tǒng)一攔截所有圖片
- 但是還是不能亂換亂用,只是有些特殊功能可以做一下,亂用的話就不好了