首页 > 代码库 > #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监听法,常见错误)