首页 > 代码库 > #iOS开发常用方法集锦#KVO(模板,setter监听法,常见错误)
#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>
// 被监听的BOOL@property
@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,转载请注明出处。
?
#iOS开发常用方法集锦#KVO(模板,setter监听法,常见错误)