#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獲取成員變量列表,它返回的是一個指針指向的是最前面的那一個
// ([CYPerson class], &count)這里面放的是類狡忙,你把類給我庶骄,我就獲取里面所有的成員變量。&count放的是成員變量的個數(shù)
for (int i = 0; i < count; i++) {
// for循環(huán)把所有成員變量遍歷出來
Ivar ivar = ivars[i]; // *(ivars + i)
// 取出每一個成員變量Ivar胡嘿,而且Ivar是以數(shù)組的形式去訪問
// ivars[i]的寫法相當(dāng)于*(ivars + i)---跳到下一個疏魏,取出這個指針指向的東西
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
// %s是因為這里是C語言的東西
// ivar_getName(ivar)這是去獲取所有成員變量的名字停做,這樣就挖掘到了所有成員變量
// ivar_getTypeEncoding(ivar)表示獲取一個成員變量的類型字符串
// 而且今后如果你有要獲取所有什么double,int類型的成員變量大莫,這種方式蛉腌,一打印就出來了
}
free(ivars);
// 這里是通過copy出來的,所以需要釋放的
}
// 這里獲取所有屬性只厘,和上面一樣
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é)議列表烙丛,這樣可以獲取這個類遵循的所有協(xié)議
// class_copyMethodList([CYPerson class], &count);是獲取方法列表,這樣可以知道這個類遵循 那些方法
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#>)
// 方法交換---這個方法的意義在于我可以將蘋果系統(tǒng)內(nèi)部的方法替換成我自己的方法懈凹。當(dāng)你認(rèn)為系統(tǒng)自帶的方法不好用時蜀变,你就將它替換為自己的方法
}
return 0;
}
*
*
*
*
*
*
- 運行時還可以動態(tài)添加成員變量方法,動態(tài)添加方法介评,動態(tài)添加屬性库北,動態(tài)添加協(xié)議這四個方法
- 假如說在代碼里面你只寫了如上幾個屬性,但是在我動態(tài)添加后们陆,在程序運行過程中會無緣無故多一個屬性出來寒瓦,但是你在代碼中是看不見的。這就是運行時能干的事情坪仇,相當(dāng)于操縱你的內(nèi)存杂腰,但是這也僅僅是運行時的冰山一角
*
*
method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
// 方法交換---這個方法的意義在于我可以將蘋果系統(tǒng)內(nèi)部的方法替換成我自己的方法。當(dāng)你認(rèn)為系統(tǒng)自帶的方法不好用時椅文,你就將它替換為自己的方法
-
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];
- 可以看出來:我調(diào)用run喂很,它執(zhí)行的study。我調(diào)用study皆刺,它執(zhí)行的run
- 那這個有啥用呢少辣?
- 在這里看出來是沒什么卵用,但是它的價值就在于可以交換系統(tǒng)自帶的方法
- 比如說我可以讓控制器的viewDidLoad方法換成我自己的viewDidLoad方法羡蛾,讓它執(zhí)行的時候執(zhí)行到我自己的方法中去了漓帅。
- 下面舉個例子:
- 我想監(jiān)控控制器什么時候死亡,那我就得重寫dealloc方法。那么假如我們的項目中有30個或者更多的控制器忙干,我想看它死掉沒有器予,防止循環(huán)引用,那我是不是20個都得實現(xiàn)dealloc方法捐迫?那我何不再搞一個方法出來乾翔,比如說CY_dealloc:方法。你本來想執(zhí)行dealloc的弓乙,現(xiàn)在執(zhí)行到我CY_dealloc:方法里去末融。那我就可以在這里統(tǒng)一監(jiān)控所有控制器的死亡
- 下面我就做一下這個事情。為了保證所有控制器都有暇韧,我弄一個控制器的分類--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來到了我CY_dealloc里面,我可以統(tǒng)一監(jiān)控所有控制器
- 上面我沒有導(dǎo)入頭文件浓瞪,是因為load方法是只要載入到內(nèi)存就會調(diào)一次懈玻,這個分類文件肯定是要裝入內(nèi)存的,所以頭文件要不要都無所謂乾颁,沒什么價值涂乌。但是你的導(dǎo)入#import <UIKit/UIKit.h>。因為我得拿控制器的名字
- 我在Main.storyboard中弄多一些控制器會發(fā)現(xiàn):
- 這樣的話英岭,任何一個控制器掛了我都能監(jiān)控得到
- 這里有一個小問題:
- 我在viewController.m文件中加上dealloc
- (void)dealloc
{
NSLog(@"-------dealloc");
}
- 然后讓上圖中的第二個控制器class為我們的viewController湾盒,運行打印,會發(fā)現(xiàn)
- 按理來說诅妹,一運行點擊button后點擊返回是應(yīng)該調(diào)用dealloc方法罚勾,但是一調(diào)用它是應(yīng)該來到我們的CY_dealloc方法,應(yīng)該是只打印出CY_dealloc的吭狡,但是打印出來還是調(diào)用了dealloc方法尖殃。這是為什么呢?
- 你會發(fā)現(xiàn)划煮,我們開始寫Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));的時候送丰,開始如果寫的是@selector(@"dealloc"),這是禁止的弛秋,說明這個方法ARC情況下是比較特殊的器躏。但是前面@selector(run)和@selector(study)它們交換方法后,只打印了對方的方法蟹略,自己的方法就不會調(diào)用了登失。
- 但是這里這么弄也正是我要說的效果:用我們的方法交換系統(tǒng)的方法后還是想辦到系統(tǒng)的方法還是能調(diào)。因為我們有時候是想在控制器死掉的時候做一些事情科乎。比如說:在控制器死掉的時候清?掉一些東西壁畸,或者在控制器死掉的時候做一些請求:取消通知
- (void)dealloc
{
NSLog(@"-------dealloc");
self.data = nil;
self.images = nil;
[self cancel];
}
- 但是如果按照開始的做法,我把dealloc換成CY_dealloc,就意味著我以后調(diào)dealloc只會來到CY_dealloc捏萍,以前系統(tǒng)的dealloc就沒法執(zhí)行太抓。但是這里上面系統(tǒng)的dealloc可以執(zhí)行,但我告訴你如果換成了普通的方法它是不可能執(zhí)行的令杈。就行前面我執(zhí)行run的時候它不可能執(zhí)行study走敌,這兩個方法只會執(zhí)行它對應(yīng)的方法
- 所以今后你如果做了方法的調(diào)換,不是系統(tǒng)的逗噩,我說的是普通的方法換了掉丽。你如果調(diào)換后還想調(diào)回以前的方法,那就做一件事情執(zhí)行以下:(self.對應(yīng)的方法)异雁。只有這樣它才會又調(diào)回自己以前的方法捶障。因為你這樣僅僅是做一個攔截嘛!就像下面:
- (void)CY_dealloc
{
NSLog(@"%@ - CY_dealloc", self);
[self CY_dealloc];
// 這樣調(diào)用的是dealloc纲刀,又調(diào)回去了
}
- 這里的意思:當(dāng)一個控制器死掉的時候项炼,它肯定會調(diào)dealloc,但dealloc方法實現(xiàn)被我換掉了示绊。所以它來到了CY_dealloc锭部。執(zhí)行完- (void)CY_dealloc
{
NSLog(@"%@ - CY_dealloc", self);這段代碼,我再調(diào)用[self CY_dealloc];面褐,它的方法實現(xiàn)是dealloc拌禾,所以又調(diào)回去了。所以今后你把系統(tǒng)方法換掉以后展哭,我建議你再調(diào)回去湃窍,當(dāng)然有些特殊情況就特殊處理。
- 那這個有什么價值呢摄杂?
- 第一個以及體現(xiàn)了價值:攔截系統(tǒng)方法調(diào)用坝咐,我就可以在系統(tǒng)方法調(diào)用之前或者調(diào)用之后做一些事情
- 第二個價值舉個例子:
- 使用字典和數(shù)組的時候一直有一個頭疼的問題:字典和數(shù)組賦值的時候不能為空,否則報錯: object cannot be nil'
NSString *value = nil;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
// dict[@"name"] = value;
NSMutableArray *array = [NSMutableArray array];
[array addObject:value];
array[0] = value;
- 但是我們很多時候不知道別人給你傳的值是不是空拔龌帧墨坚!為了不報錯,我們經(jīng)常都得進(jìn)行判斷:如果value有值映挂,我再執(zhí)行泽篮。
- 那有沒有一個辦法可以過濾掉這些東西呢?只要發(fā)現(xiàn)傳進(jìn)來的值是個空的柑船,我就不處理帽撑,那可不可以一勞永逸做到這點呢?我們只要寫下某一段代碼鞍时,以后凡是項目中傳空的值進(jìn)來亏拉,它都不報錯了扣蜻,怎么去做呢?
- 只要將NSMutable--的addObject:方法換掉及塘,攔截這個方法莽使,鴨蛋發(fā)現(xiàn)傳進(jìn)來的值是空的我就不調(diào),如果傳進(jìn)來的值不是nil笙僚,我就調(diào)回它以前的方法芳肌。那么這樣的話今后數(shù)組的訪問或者字典的訪問就不會存在傳進(jìn)來值為空報錯的問題了
- 還舉個例子:[UIImage imageName:我完全可以將imageName:方法攔截一下,我就可以對它傳進(jìn)來的圖片名進(jìn)行統(tǒng)一處理肋层,比如說什么夜間模式亿笤,什么皮膚,我就可以根據(jù)文件名去加載另外一個文件栋猖,統(tǒng)一攔截所有圖片
- 但是還是不能亂換亂用净薛,只是有些特殊功能可以做一下,亂用的話就不好了