6.4 蒼老師的資料歸檔篇
蒼老師的資料總要整理一下吧!
創建People.h
#import <Foundation/Foundation.h>
@interface People : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name; // 姓名
@property (nonatomic, strong) NSNumber *age; // 年齡
@property (nonatomic, copy) NSString *occupation; // 職業
@property (nonatomic, copy) NSString *nationality; // 國籍
@end
People.m
#import "People.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation People
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([People class], &count);
for (NSUInteger i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivars);
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([People class], &count);
for (NSUInteger i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
@end
6.5 蒼老師的資料轉換篇
服務器返回了大量蒼老師的數據,手機端這邊接收後如何去轉換呢?當然是要將JSON轉換爲Model啦!
相信平時你們的項目中也用到過這些三方庫,下面我們去了解下runtime實現JSON和Model互轉。
創建People.h
#import <Foundation/Foundation.h>
@interface People : NSObject
@property (nonatomic, copy) NSString *name; // 姓名
@property (nonatomic, strong) NSNumber *age; // 年齡
@property (nonatomic, copy) NSString *occupation; // 職業
@property (nonatomic, copy) NSString *nationality; // 國籍
// 生成model
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
// 轉換成字典
- (NSDictionary *)covertToDictionary;
@end
People.m的代碼如下:
#import "People.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation People
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
for (NSString *key in dictionary.allKeys) {
id value = dictionary[key];
SEL setter = [self propertySetterByKey:key];
if (setter) {
// 這裏還可以使用NSInvocation或者method_invoke,不再繼續深究了,有興趣google。
((void (*)(id, SEL, id))objc_msgSend)(self, setter, value);
}
}
}
return self;
}
- (NSDictionary *)covertToDictionary
{
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([self class], &count);
if (count != 0) {
NSMutableDictionary *resultDict = [<a href="http://www.jobbole.com/members/www821839432">@{}</a> mutableCopy];
for (NSUInteger i = 0; i < count; i ++) {
const void *propertyName = property_getName(properties[i]);
NSString *name = [NSString stringWithUTF8String:propertyName];
SEL getter = [self propertyGetterByKey:name];
if (getter) {
id value = ((id (*)(id, SEL))objc_msgSend)(self, getter);
if (value) {
resultDict[name] = value;
} else {
resultDict[name] = @"字典的key對應的value不能爲nil哦!";
}
}
}
free(properties);
return resultDict;
}
free(properties);
return nil;
}
#pragma mark - private methods
// 生成setter方法
- (SEL)propertySetterByKey:(NSString *)key
{
// 首字母大寫,你懂得
NSString *propertySetterName = [NSString stringWithFormat:@"set%@:", key.capitalizedString];
SEL setter = NSSelectorFromString(propertySetterName);
if ([self respondsToSelector:setter]) {
return setter;
}
return nil;
}
// 生成getter方法
- (SEL)propertyGetterByKey:(NSString *)key
{
SEL getter = NSSelectorFromString(key);
if ([self respondsToSelector:getter]) {
return getter;
}
return nil;
}
@end
main.m中運行以下代碼:
#import <Foundation/Foundation.h>
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
#import "People.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *dict = @{
@"name" : @"蒼井空",
@"age" : @18,
@"occupation" : @"老師",
@"nationality" : @"日本"
};
// 字典轉模型
People *cangTeacher = [[People alloc] initWithDictionary:dict];
NSLog(@"熱烈歡迎,從%@遠道而來的%@歲的%@%@",cangTeacher.nationality,cangTeacher.age,cangTeacher.name,cangTeacher.occupation);
// 模型轉字典
NSDictionary *covertedDict = [cangTeacher covertToDictionary];
NSLog(@"%@",covertedDict);
}
return 0;
}
最後輸出內容如下:
相信通過前面的學習,這些代碼不用寫過多的註釋你也可以看懂了,我把假設是網絡返回的蒼老師的資料轉化爲了model,然後又將model轉回字典。這是一個JSON轉Model相互轉換的一個思路,大家稍後運行Demo細細品味。
6.6 蒼老師的唱歌篇
這個實例主要是驗證一下上文《5.2 消息動態解析》
第一首:
添加sing實例方法,但是不提供方法的實現。驗證當找不到方法的實現時,動態添加方法。
創建People.h
#import <Foundation/Foundation.h>
@interface People : NSObject
@property (nonatomic, copy) NSString *name;
- (void)sing;
@end
創建People.m
#import "People.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation People
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// 我們沒有給People類聲明sing方法,我們這裏動態添加方法
if ([NSStringFromSelector(sel) isEqualToString:@"sing"]) {
class_addMethod(self, sel, (IMP)otherSing, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void otherSing(id self, SEL cmd)
{
NSLog(@"%@ 唱歌啦!",((People *)self).name);
}
在main.m中運行以下代碼:
#import <Foundation/Foundation.h>
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
#import "People.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
People *cangTeacher = [[People alloc] init];
cangTeacher.name = @"蒼老師";
[cangTeacher sing];
}
return 0;
}
結果如下:
我們沒有提供蒼老師唱歌的方法實現,因此在調用此方法的時候,會調用resolveInstanceMethod方法,我們動態添加了方法。我們也可以返回No,繼續向下傳遞。(此處請返回《5.2 消息動態解析》第一步品味下)
第二首
外面的小鳥在唱歌,但是蒼老師的歌聲蓋過了小鳥,我們只能聽到蒼老師唱歌了。
這裏我們不聲明sing方法,將調用途中動態更換調用對象。
在第一首代碼的基礎上,創建Bird的model
Bird.h
#import <Foundation/Foundation.h>
@interface Bird : NSObject
@property (nonatomic, copy) NSString *name;
@end
Bird.m
#import "Bird.h"
#import "People.h"
@implementation Bird
// 第一步:我們不動態添加方法,返回NO,進入第二步;
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
return NO;
}
// 第二部:我們不指定備選對象響應aSelector,進入第三步;
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
// 第三步:返回方法選擇器,然後進入第四部;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"sing"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 第四部:這步我們修改調用對象
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// 我們改變調用對象爲People
People *cangTeacher = [[People alloc] init];
cangTeacher.name = @"蒼老師";
[anInvocation invokeWithTarget:cangTeacher];
}
@end
main.m運行代碼如下:
#import <Foundation/Foundation.h>
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
#import "People.h"
#import "Bird.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Bird *bird = [[Bird alloc] init];
bird.name = @"小小鳥";
((void (*)(id, SEL))objc_msgSend)((id)bird, @selector(sing));
}
return 0;
}
運行結果如下:
成功更換了對象,把對象更換爲蒼老師了。(此處請返回《5.2 消息動態解析》品味)
第三首
蒼老師不想唱歌想跳舞了。
這裏我是實現不提供聲明,不修改調用對象,但是將sing方法修改爲dance方法。
創建People.h
#import <Foundation/Foundation.h>
@interface People : NSObject
@end
People.m
#import "People.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation People
// 第一步:我們不動態添加方法,返回NO,進入第二步;
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
return NO;
}
// 第二部:我們不指定備選對象響應aSelector,進入第三步;
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
// 第三步:返回方法選擇器,然後進入第四部;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"sing"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 第四部:這步我們修改調用方法
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setSelector:@selector(dance)];
// 這還要指定是哪個對象的方法
[anInvocation invokeWithTarget:self];
}
// 若forwardInvocation沒有實現,則會調用此方法
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
NSLog(@"消息無法處理:%@", NSStringFromSelector(aSelector));
}
- (void)dance
{
NSLog(@"跳舞!!!come on!");
}
@end
在main.m中運行如下代碼:
#import <Foundation/Foundation.h>
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
#import "People.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
People *cangTeacher = [[People alloc] init];
((void(*)(id, SEL)) objc_msgSend)((id)cangTeacher, @selector(sing));
}
return 0;
}
結果如圖:
成功更換了方法,蒼老師由唱歌改爲跳舞了(此處請返回《5.2 消息動態解析》品味)