基本參數(shù)類型
SEL:類成員方法的指針,但不同于C語言中的函數(shù)指針绷杜,函數(shù)指針直接保存了方法的地址直秆,但SEL只是方法編號。
IMP:一個函數(shù)指針,保存了方法的地址
Method:方法的結(jié)構(gòu)體鞭盟,其中保存了方法的名字圾结,實現(xiàn)和類型描述字符串
1. 獲取所有屬性
只能獲取由 @property
聲明的屬性,不管是不是私有屬性,獲取的值是不帶下劃線的.
+ (NSArray *)getAllProperties{
u_int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *propertiesArrayM = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; i++) {
//此刻得到的propertyName為c語言的字符串
objc_property_t property = properties[i];
const char* propertyName = property_getName(property);
//此步驟把c語言的字符串轉(zhuǎn)換為OC的NSString
[propertiesArrayM addObject: [NSString stringWithUTF8String: propertyName]];
}
//class_copyPropertyList底層為C語言,所以我們一定要記得釋放properties
free(properties);
return propertiesArrayM;
}
2.獲取所有成員變量
獲取所有帶下劃線的成員變量,獲取的值帶下劃線.
+ (NSArray *)getAllMemberVariables{
u_int count; //成員變量個數(shù)
Ivar *vars = class_copyIvarList([self class], &count);
NSMutableArray *memberVariablesArrayM = [NSMutableArray arrayWithCapacity:count];
NSString *name=nil;
// NSString *type=nil;
for(int i = 0; i < count; i++) {
Ivar thisIvar = vars[i];
const char* ivarName = ivar_getName(thisIvar);
name = [NSString stringWithUTF8String: ivarName]; //獲取成員變量的名字
[memberVariablesArrayM addObject:key];
// type = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisIvar)]; //獲取成員變量的類型
}
free(vars);
return memberVariablesArrayM;
}
3.獲取方法名稱
只能獲取到 已經(jīng)實現(xiàn)的實例對象 方法名稱,包括Category
中.只聲明未實現(xiàn)的該方法查詢不出來.注意: 因為@property
聲明的屬性是默認(rèn)實現(xiàn)了get
和 set
方法的, 所以便利出來的方法中包含.
+ (NSArray *)getAllMethods{
u_int count;
Method *methodList = class_copyMethodList([self class], &count);
NSMutableArray *methodArrayM = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; i++) {
Method temp = methodList[i];
SEL name_f = method_getName(temp); // 方法的編號
// 方法名
const char* name_s = sel_getName(name_f);
[methodArrayM addObject:[NSString stringWithUTF8String:name_s]];
}
free(methodList);
return methodArrayM;
}
補充:
method
相關(guān)函數(shù)
獲取具體實現(xiàn)的指針 返回值類型為 IMP
method_getImplementation(Method m)
獲取參數(shù)個數(shù)(因為每一個方法會默認(rèn)隱藏兩個參數(shù)齿诉,self筝野、_cmd晌姚,self代表方法調(diào)用者,_cmd代表這個方法的SEL) 所以參數(shù)的個數(shù)都是2起步
返回值類型為 int
method_getNumberOfArguments(Method m)
獲取返回值類型 返回值為字符串 例: 'B','v,'@','d'......等等 返回值的具體含義詳見 官網(wǎng)
返回值類型為 char *
method_copyReturnType(Method m)
獲取參數(shù)類型 按腳標(biāo)獲取 注意,有兩個默認(rèn)參數(shù),自定義參數(shù)從第三個開始.
返回值類型為char *
method_copyArgumentType(Method m, unsigned int index)
獲取 class_copyProtocolList:
拷貝協(xié)議列表歇竟。返回的一個Ivar列表的指針挥唠。獲取Ivar需要遍歷這個列表。
注意:調(diào)用copy的函數(shù)需要釋放資源free();
此函數(shù)不能獲取分類中添加的協(xié)議焕议。
此函數(shù)可以獲取動態(tài)添加的協(xié)議宝磨。
4.獲取當(dāng)前對象的屬性和屬性值
該方法只能獲取到 @property
聲明的屬性和屬性值
如果需要獲取成員變量及值的話 參照 2
需要注意的是上述方法獲取到是帶下劃線的成員變量名稱,所以通過kvc獲取對應(yīng)的value時 需要注意把下劃線去掉.
- (NSDictionary *)getAllPropertiesAndVaules{
NSMutableDictionary *propsDic = [NSMutableDictionary dictionary];
u_int outCount;
objc_property_t *properties =class_copyPropertyList([self class], &outCount);
for ( int i = 0; i<outCount; i++)
{
objc_property_t property = properties[i];
const char* char_f =property_getName(property);
NSString *propertyName = [NSString stringWithUTF8String:char_f]; // 屬性名稱
id propertyValue = [self valueForKey:propertyName]; //屬性值
[propsDic setObject:propertyValue?:@"nil" forKey:propertyName];
}
free(properties);
return propsDic;
}
5. 關(guān)聯(lián)對象
runtime的關(guān)聯(lián)可以讓我們動態(tài)的添加屬性,也就是 Category
添加屬性
//1. 聲明屬性
@property (nonatomic,assign) BOOL testProperty;
//2. 定義一個常量key 必須是C語言字符串
static char *TestPropertyKey = "TestPropertyKey";
//3. 實現(xiàn) set 方法
- (void)setTestProperty:(BOOL)testProperty{
objc_setAssociatedObject(self, TestPropertyKey, [NSNumber numberWithBool:NO], OBJC_ASSOCIATION_ASSIGN);
}
//4. 實現(xiàn) get 方法
- (BOOL)testProperty{
return [objc_getAssociatedObject(self, TestPropertyKey) boolValue];
}
補充:
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
參數(shù):
id object
: 表示關(guān)聯(lián)者,是一個對象号坡,也就是給誰添加屬性值
const void *key
: 獲取被關(guān)聯(lián)者的索引key
id value
: 被關(guān)聯(lián)者懊烤,也就是屬性值 id類型.上面是一個BOOL值 所以轉(zhuǎn)換成NSNumber
objc_AssociationPolicy policy
: 關(guān)聯(lián)時采用的協(xié)議
關(guān)聯(lián)協(xié)議類型:
OBJC_ASSOCIATION_ASSIGN
等價于 @property(assign)梯醒。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
等價于 @property(nonatomic, strong)宽堆。
OBJC_ASSOCIATION_COPY_NONATOMIC
等價于@property(nonatomic, copy)。
OBJC_ASSOCIATION_RETAIN
等價于@property(atomic, strong)茸习。
OBJC_ASSOCIATION_COPY
等價于@property(atomic, copy)畜隶。
objc_getAssociatedObject(id object, const void * key)
參數(shù):
id object
: 關(guān)聯(lián)者,是一個對象
const void *key
: 獲取被關(guān)聯(lián)者的索引key
存進(jìn)去的是什么值,獲取的就是什么值.
移除關(guān)聯(lián)對象
該方法將會移除關(guān)聯(lián)對象中所有的被關(guān)聯(lián)者. 參數(shù)為關(guān)聯(lián)對象.
objc_removeAssociatedObjects(id _Nonnull object)
所以如果需要移除單個被關(guān)聯(lián)者 那么只需要使用 objc_setAssociatedObject
將 value
設(shè)為 nil
就OK了.
6.交換方法 method swizzling (俗稱:黑魔法)
Method Swizzling原理
Method Swizzing是發(fā)生在運行時的号胚,主要用于在運行時將兩個Method進(jìn)行交換籽慢,我們可以將Method Swizzling代碼寫到任何地方,但是只有在這段Method Swilzzling代碼執(zhí)行完畢之后互換才起作用猫胁。
獲取類方法 class_getClassMethod
Method classMethod = class_getClassMethod([self class], @selector(testClassAction));
獲取實例方法 class_getInstanceMethod
Method instanceMethod = class_getInstanceMethod([self class], @selector(testObjcAction));
Method Swizzling 方法攪拌
method_exchangeImplementations(Method m1, Method m2)
實現(xiàn)
Method m1 = class_getInstanceMethod([self class], @selector(testObjcAction1));
Method m2 = class_getInstanceMethod([self class], @selector(testObjcAction2));
method_exchangeImplementations(m1, m2);
- (void)testObjcAction1{
NSLog(@"testObjcAction1");
}
- (void)testObjcAction2{
NSLog(@"testObjcAction2");
}
補充:
使用 method_exchangeImplementations
交換方法實現(xiàn)時箱亿,只要 Method 參數(shù)不為 nil 那么都能替換.如果 Method 參數(shù)有一個為nil 那么就會交換.
其中 類方法與實例方法可以交換,正常執(zhí)行不會報錯和崩潰, 但是如果交換的方法中 使用到了類或者實例對象獨有的方法,那么就會崩潰. 因為雖然交換了方法的實現(xiàn), 但本質(zhì)執(zhí)行這個的對象沒有沒有變. 類還是類,對象還是對象.
參數(shù)不統(tǒng)一 編譯時不會報錯弃秆,但是運行時會報錯届惋。因為運行時最后找到的方法實現(xiàn)與調(diào)用的類型不一致 會造成崩潰.
什么時候需要交換方法了 就什么時候執(zhí)行代碼. 如果是在 Category
中替換方法,且是一開始就要替換的,那么建議在 + (void)load
中替換,因為這個方法在你導(dǎo)入當(dāng)前文件時就會執(zhí)行.建議加上dispatch_once
來保證替換代碼只會執(zhí)行一次.
替換方法前,確定方法已經(jīng)實現(xiàn).防止調(diào)用時崩潰
后續(xù)會持續(xù)更新.上述內(nèi)容都是壯骨自己理解的,如果有不對的地方,歡迎大家在評論交流下,謝謝