#iOS开发常用方法集锦#KVO(模板,setter监听法,常见错误)

Evernote印象笔记:https://app.yinxiang.com/l/ABZgicPELllCaLkuZIkHemnyOcDLOMx8M9Y

本文永久地址为?http://www.cnblogs.com/ChenYilong/p/4107538.html,转载请注明出处。

?

KVO模板

?

BOOL类型的KVO监听

?

? ??<#Observer_Class#>?*[<#whoIsObserveredObject#>?=?[[<#Observer_Class#>?alloc]?init];

? ??// KVO注册监听

? ??[<#Observer_Object#>??addObserver:<#IsObserveredObject#>?forKeyPath:@"<#KeyPath#>"?options:NSKeyValueObservingOptionNew?context:nil];

? ??// 属性赋值,手动调用observeValueForKeyPath:ofObject:change:context:

? ??[<#IsObserveredObject#>?observeValueForKeyPath:nil?ofObject:nil?change:nil?context:nil];

?

// KVO监听执行

-?(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context?{??

if?([keyPath?isEqualToString:@"<#KeyPath#>"])?{??

? ??BOOL?<#boolValue#>?=?[change[NSKeyValueChangeNewKey]?boolValue];??

? ??<#Observer_Class#>?*Observer_Object?=?object;??

//...??

}

?

}

-?(void)dealloc?{

? ??// KVO反注册

? ??for?(<#Observer_Class#>?*<#Observer_Object#>?in?<#KVO_<obj_Array#>)?{

? ? ? ??[<#Observer_Object#>?removeObserver:self?forKeyPath:@"<#KeyPath#>"];

? ??}

}

?

C数据的KVO监听

?

要包装为NSValue,下面以CLLocationCoordinate2D(struct)为例?例子来源

http://stackoverflow.com/questions/5691881/how-to-wrap-a-struct-into-nsobject

@property?(assign,?nonatomic)?CLLocationCoordinate2D?regionCenter;

//顺便附上如何将CLLocationCoordinate2D封装为NSValue

// NSValue *anObj = [NSValue value:&_regionCenter withObjCType:@encode(CLLocationCoordinate2D)];

-(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context?{

? ??if?([keyPath?isEqualToString:@"regionCenter"])?{

? ? ? ??// 从NSValue中取出CLLocationCoordinate2D(struck)赋值到newRegionCenter

? ? ? ??NSValue?*newCenter?=?change[NSKeyValueChangeNewKey];

? ? ? ??CLLocationCoordinate2D?newRegionCenter;

? ? ? ??[newCenter?getValue:&newRegionCenter];

// ...

? ??}

}

或者直接借助方法

struct?_Pair

{

? ??short?val;

? ??short?count;

};

typedef?struct?_Pair?Pair;

?

@interface?NSValue?(Pair)

+?(id)valueWithPair:(Pair)pair;

-?(Pair)pairValue;

@end

@implementation?NSValue?(Pair)

+?(id)valueWithPair:(Pair)pair

{

? ??return?[NSValue?value:&pair?withObjCType:@encode(Pair)];

}

-?(Pair)pairValue

{

? ??Pair?pair;?

? ??[self?getValue:&pair];?

? ??return?pair;

}

@end

参见

http://stackoverflow.com/questions/5691881/how-to-wrap-a-struct-into-nsobject

?

setter方法中注册监听

?

先是四个特点?@property监听并改变self的@property?被监听者反客为主?监听者与被监听者@property相同,山寨啊!?监听者是自定义的,有@property的,foundation不行

特定?:

@property对象监听self的其他@property(自定义,非Foundation对象),用自己(@property)的@property直接改变self的@property或者是借由类似[self?setImage:_item.selectedImage?forState:UIControlStateSelected];

.的方法来设置self的绑定有状态的@property属性.

(@property对象setter中监听self的@property,并借用setter方法用@property(自己)的@property改变self的@property.)

特定?:

observeValueForKeyPath:ofObject:change:context:方法中是被监听者在调用方法!!监听者(@property)反而成了配角!!很反常用木有!!

特定?:

自定义的@property的@property应当和被监听的self的@property相同或者相乎对应.比如image属性对应于self的[self?setImage:_item.selectedImage?forState:UIControlStateNormal];

而selectedImage对应于[self?setImage:_item.selectedImage?forState:UIControlStateSelected];

注册监听该setter对应的@property对象(自定义的,非Foundation对象),

监听实现部分是在改变@property对象(自定义的,非Foundation对象)属性.

特定?:

用法说明,仅仅在@property对象是非Foundation对象,且监听执行方法,与该@property对象(非Foundation对象)的属性相关时

***

举例说明用法?例子来源: https://github.com/bradtheappguy/picbounce2-iphone/blob/master/PBUploadingTableViewCell.m

/代码/ItcastWeibo/ItcastWeibo/Classes/Main/View?中的IWTabBarButton.m?或者

?

@interface?IWTabBarButton?:?UIButton

// @property声明

@property?(nonatomic,?strong)?UITabBarItem?*item;

@end

-?(void)setItem:(UITabBarItem?*)item

{

? ??_item?=?item;

? ??// setter方法中注册监听self的title等属性,只用当@property(自己)改变时才注册监听,

? ??// 注意这里监听的是item而非_item(虽然在_item=item赋值后,效果一样,但是意义是不一样的),如果是_item,那么下面的四行代码可以省略.直接保留[self observeValueForKeyPath:nil ofObject:nil change:nil context:nil];

? ??// 并且这需要保证item(而非_item)是先alloc_init而且设置完@property后就行的setter操作

? ??// 仅仅保留最后的[self observeValueForKeyPath:nil ofObject:nil change:nil context:nil];

? ??// 注册监听的好处:item可以先alloc_init然后再设置@property.不这样操作,而引起的错误可参考https://app.yinxiang.com/l/ABaqZ1e2XJBHIJxZU9MhnNdbtjg3u8DZNqw

? ??[item?addObserver:self?forKeyPath:@"title"?options:NSKeyValueObservingOptionNew?context:nil];

? ??[item?addObserver:self?forKeyPath:@"image"?options:NSKeyValueObservingOptionNew?context:nil];

? ??[item?addObserver:self?forKeyPath:@"selectedImage"?options:NSKeyValueObservingOptionNew?context:nil];

? ??[item?addObserver:self?forKeyPath:@"badgeValue"?options:NSKeyValueObservingOptionNew?context:nil];

? ??// 属性赋值,必须有,尤其当item(而非_item)先alloc再设置其@property时,搭配(_item=item)使用.

? ??// 作用在于不需要在(observeValueForKeyPath:ofObject:change:context:)之前设置被监听的属性(@"title",:@"image",@"selectedImage",@"badgeValue")

? ??// 可以在第一次值未变化就调用KVO监听,否则必须在(observeValueForKeyPath:ofObject:change:context:)之前设置被监听的属性(@"title",:@"image",@"selectedImage",@"badgeValue")

? ??[self?observeValueForKeyPath:nil?ofObject:nil?change:nil?context:nil];

}

-?(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context

{

? ??// 监听实现方法中,用@property(自己)的@property改变self的@property

? ??[self?setImage:_item.image?forState:UIControlStateNormal];

? ??[self?setImage:_item.selectedImage?forState:UIControlStateSelected];

? ??[self?setTitle:_item.title?forState:UIControlStateNormal];

? ??if?([_item.badgeValue?intValue])?{

? ? ? ??CGSize?badgeSize?=?[_item.badgeValue?sizeWithFont:_badge.titleLabel.font];

? ? ? ??CGFloat?w?=?badgeSize.width?+?11;

? ? ? ??CGFloat?h?=?badgeSize.height?+?5;

? ? ? ??CGFloat?x?=?self.frame.size.width?-?w?-?5;

? ? ? ??_badge.frame?=?CGRectMake(x,?2,?w,?h);

? ? ? ??[_badge?setTitle:_item.badgeValue?forState:UIControlStateNormal];

? ? ? ??_badge.hidden?=?NO;

? ??}?else?{

? ? ? ??_badge.hidden?=?YES;

? ??}

}

?

举例说明用法,?分为?KVO注册监听?KVO监听并执行相应方法?KVO反注册?例子:

https://github.com/opedge/OMAMovingAnnotations

?

?注册部分

?

@interface?OMAMovingAnnotation?:?NSObject?<MKAnnotation>

// 被监听的[email protected]

@property?(nonatomic,?assign,?getter?=?isMoving)?BOOL?moving;

// 其他相关的@property和方法声明

@property?(nonatomic,?assign)?CLLocationCoordinate2D?coordinate;

@property?(nonatomic,?strong)?OMAMovePath?*movePath;

@property?(nonatomic,?assign,?readonly)?OMAMovePathSegment?currentSegment;

@property?(nonatomic,?assign,?readonly)?float?angle;

-?(void)moveStep;

@end

? ??for?(NSInteger?i?=?0;?i?<?OMAViewControllerAnnotationsCount;?i++)?{

? ? ? ??CLLocationCoordinate2D?start?=?[self?randomCoordinateInRegion:self.region];

? ? ? ??CLLocationCoordinate2D?end?=?[self?randomCoordinateInRegion:self.region];

? ? ? ??[path?addSegment:OMAMovePathSegmentMake(start,?end,?[self?randomDoubleBetweenMin:5?max:10])];

?

? ? ? ??OMAMovingAnnotation?*annotation?=?[[OMAMovingAnnotation?alloc]?init];

? ? ? ??annotation.coordinate?=?start;

? ? ? ??annotation.movePath?=?path;

? ? ? ??// KVO注册监听

? ? ? ??[annotation?addObserver:self?forKeyPath:@"moving"?options:NSKeyValueObservingOptionNew?context:NULL];

? ??}

?

?KVO监听并执行相应方法

?

// KVO监听并执行相应方法

-?(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context?{

? ??if?([keyPath?isEqualToString:@"moving"])?{

? ? ? ??BOOL?moving?=?[change[NSKeyValueChangeNewKey]?boolValue];

? ? ? ??if?(!moving)?{

? ? ? ? ? ??OMAMovingAnnotation?*annotation?=?object;

? ? ? ? ? ??if?([annotation.movePath?isEmpty]?&&?OMAMovePathSegmentIsNull(annotation.currentSegment))?{

? ? ? ? ? ? ? ??OMAMovePathSegment?segment?=?OMAMovePathSegmentMake(annotation.coordinate,?[self?randomCoordinateInRegion:self.region],?[self?randomDoubleBetweenMin:5?max:10]);

? ? ? ? ? ? ? ??[annotation.movePath?addSegment:segment];

? ? ? ? ? ??}

? ? ? ??}

? ??}?else?{

? ? ? ??[super?observeValueForKeyPath:keyPath?ofObject:object?change:change?context:context];

? ??}

}

?

?KVO反注册

?

// KVO反注册

-?(void)dealloc?{

? ??for?(OMAMovingAnnotation?*annotation?in?self.mapView.annotations)?{

? ? ? ??[annotation?removeObserver:self?forKeyPath:@"moving"];

? ??}

}

?

注意下面的错误情况

?

-(void)viewDidLoad?{

? ??[self?addObserver:self?forKeyPath:@"isDetailView"?options:NSKeyValueObservingOptionNew?context:nil];

? ??[isDetailView?setString:@"YES"];

}

?

-?(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context

{

? ??NSLog(@"obersedValueFOrKeyPath:%@",?keyPath);

}

记得在注册监听前,添加下面的代码,KVO--监听才能生效

[self?setIsDetailView:?@"YES"]

或者记得在注册监听后,添加下面的代码,KVO--监听才能生效

? ??[self?observeValueForKeyPath:nil?ofObject:nil?change:nil?context:nil];

参考文献:

?

https://app.yinxiang.com/l/ABaqZ1e2XJBHIJxZU9MhnNdbtjg3u8DZNqw

?

Evernote印象笔记:https://app.yinxiang.com/l/ABZgicPELllCaLkuZIkHemnyOcDLOMx8M9Y

本文永久地址为?http://www.cnblogs.com/ChenYilong/p/4107538.html,转载请注明出处。

?

时间: 2024-10-07 12:54:09

#iOS开发常用方法集锦#KVO(模板,setter监听法,常见错误)的相关文章

#iOS开发常用方法集锦#为UITableView添加UISwipeGestureRecognizer手势

? 本文永久地址为http://www.cnblogs.com/ChenYilong/p/4103039.html ,转载请注明出处. 印象笔记链接:https://app.yinxiang.com/shard/s22/sh/04150175-aac6-4981-b71d-d7246de3037b/a0f139b2619a4607 ? ? ? <UIGestureRecognizerDelegate> -(void)viewDidLoad { ? ? [superviewDidLoad]; ?

#iOS开发常用方法集锦#如何为UIView添加居中背景

?本文永久地址为?http://www.cnblogs.com/ChenYilong/p/4103050.html,转载请注明出处. Evernote印象笔记:https://app.yinxiang.com/shard/s22/sh/2fe4cb0f-26cb-47ce-8569-bb45451cb7b8/6118d5a054003de9 //如何为UIView添加居中背景 #define kBackgroundImageCenterForView(ViewName,imageName)\ U

#iOS开发常用方法集锦#如何检查UITextField是否为空,以及是否为手机号

? 本文永久地址为http://www.cnblogs.com/ChenYilong/p/4107467.html?,转载请注明出处. Evernote印象笔记https://app.yinxiang.com/shard/s22/sh/9d7e4ca2-ad34-445e-b267-0fb62216c60d/6f61ffe1907cfde0 ? -(BOOL)checkTextNULL:(NSString?*)string?{ ? ??if?([string?isEqualToString:@"

#iOS开发常用方法集锦#FMDB数据库操作

本文永久地址为?http://www.cnblogs.com/ChenYilong/p/4107506.html,转载请注明出处. Evernote印象笔记:https://app.yinxiang.com/l/ABZhZU-_1SBPIoLhgZ7-WrMwJYAhD89Swn0//增删改查操作 //#import "CYLFMDatabaseTool.h" ? //插入操作 -(void)insertPhoneNumberHistoryWithFMDB:(NSArray?*)pho

ios开发笔记----exc_bad_access(code=1, address=0x789870)野指针错误,假死debug状态

错误原因: exc_bad_access(code=1, address=0x789870)野指针错误,主要的原因是,当某个对象被完全释放,也就是retainCount,引用计数为0后.再去通过该对象去调用其它的方法就会出现野指针错误. 例如: Person *jerry = [[Person alloc]init];  //  retainCount引用计数为1 [jerry eat];  //  调用吃的方法 [jerry release];  //  释放jerry这个对象到 retain

iOS开发——实用篇&amp;KVO与KVC详解

KVO与KVC详解 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听(KVO)特性: 键值编码KVC 键值监听KVO 键值编码KVC 我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象.其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Valu

IOS开发之--异常处理--使用try 和 catch 来捕获错误。

一个搞java的老板问我会不会try catch  我说不会 学这么久也没听周围朋友用这个 因为苹果控制台本来就可以打印异常 特此研究一下. 1.try catch:  是捕获异常代码段   特点:对代码的实时监控  占用大量资源 2.ios中很少用到try 和catch 简单的来说,Apple虽然同时提供了错误处理(NSError)和异常处理(exception)两种机制,但是Apple更加提倡开发者使用NSError来处理程序运行中可恢复的错误.而异常被推荐用来处理不可恢复的错误. 原因有几

Git在开发android系统时常用的技巧及常见错误解决方案

Git常用的技巧及常见错误解决方案 项目管理工作 repo 常用命令 ==目标== 了解repo工作原理及常用的repo命令 Git 日常使用的命令,常见问题及解决方案 ==关键词== Repo , Git ==工作指南== == <big>Repo</big> == Android代码其实是由若干个git 工作组织在一起的,repo 是一个便于整体管理这些项目的脚本. 拉代码时需要用到几个步骤,分别对几个步骤进行阐述一下,网上有很多repo 的使用方法,但是对其工作原理描述的并不

iOS开发-KVC和KVO的理解

KVC和KVO看起来很专业,其实用起来还是比较简单的,KVC(Key-value coding)可以理解为键值对编码,如果对象的基本类型,那么键值对编码实际上和get,set方法没有区别,如果是属性是另外一个对象,那么发现KVC用起来还是非常顺手,KVO(key-value observing)是键值对的观察者模式,如果对象的属性发生变更,那么会触发observeValueForKeyPath事件,KVO的这种通知特性让我们在开发的时候节省了不必要的代码,提高了开发效率. KVC键值对编码 KV