前几天遇到项目中要讲 BaseViewController 中的文件抽出来,里面有使用私有成员变量
,知道分类中可以访问暴露在. h 文件中成员变量
;
原来BaseViewController私有,表示也就没有意愿给自己. m 以外的地方用.
如代码所示:deciveAlertView
是一个私有的成员变量,所以将这一个方法抽到在分类中,先要解决私有成员变量的问题;
下面有几种实现方式:
-
使用一个全局变量,但是这个全局变量是保存在静态区,不会随着这个类创建的对象 dealloc 而释放,这点不太爽
-
直接修改代码逻辑,分类中就不要使用成员变量了
分析
-
category 里面的方法是 runtime 动态添加进去的,使用Associated Objects也可以动态添加进去,但是这是最后的方法,一般不想这么搞
-
使用全局不会释放,这点不太好;放弃
3.直接修改逻辑,这样能不适用成员变量;这样就使方法纯粹了,这也是分类的目的所在
那到底能不能在 category 加成员变量呢?结果是不可以的,除非用Associated;我不要听,我要试试!!!
验证 category 不能加成员变量
第一种:在.h 文件的@ interface 里面加
#import <UIKit/UIKit.h>
#import "CXAlertView.h"
@interface UIViewController (QDCXAlertView)
/** deciveAlertView*/
@property (nonatomic ,strong) CXAlertView *deciveAlertView;
-(void)deciveCodeError:(NSNotification*)notification;
@end
.m文件会报如下 warning,怎么处理呢?
你可以采取如下措施:用@ dynamic 消除编译器 warning, 使用Associated来使用 setter 和 getter;归根到底是用了Associated.
归根到底没有帮你加成员变量
UIViewController+QDCXAlertView.m:20:17: Property 'deciveAlertView' requires method 'setDeciveAlertView:' to be defined - use @dynamic or provide a method implementation in this category
UIViewController+QDCXAlertView.m:20:17: Property 'deciveAlertView' requires method 'deciveAlertView' to be defined - use @dynamic or provide a method implementation in this category
第二种:在. h 文件中的@ interface 用{}
#import <UIKit/UIKit.h>
#import "CXAlertView.h"
@interface UIViewController (QDCXAlertView){
CXAlertView *_deciveAlertView;
}
-(void)deciveCodeError:(NSNotification*)notification;
@end
UIViewController+QDCXAlertView.h:14:18: Instance variables may not be placed in categories
结果更加悲剧,直接爆红;说分类中不可以加成员变量,不死心,接着第三种
第三种:在. m 的@ implement 用{}
@implementation UIViewController (QDCXAlertView){
CXAlertView *_deciveAlertView;
}
UIViewController+QDCXAlertView.m:20:49: Expected identifier or '('
什么鬼,直接爆红,不能理解意思;好吧放弃,第四种
第四种:在. m 搞个 extension, 在 extension的@interface 用@property
@interface UIViewController ()
/** deciveAlertView*/
@property (nonatomic ,strong) CXAlertView *deciveAlertView;
@end
好像可以哦,没有报红,没有 warning. 哦也可以了!!!
等一下,我重写 getter 试试
-(CXAlertView *)deciveAlertView{
return _deciveAlertView;
}
UIViewController+QDCXAlertView.m:27:12: Use of undeclared identifier '_deciveAlertView'
又报红了,说没有定义;我靠! 白开心一场
第五种:在. m 搞个 extension, 在 extension的@interface 用{}
@interface UIViewController (){
CXAlertView *_deciveAlertView;
}
我去,可以用,没有报红,没有 warning!!!马上就要成功了,不行我要运行下试试.看行不行.我靠爆红了!!!
"_OBJC_IVAR_$_UIViewController._deciveAlertView", referenced from:
clang: error: linker command failed with exit code 1 (use -v to see invocation)
我累了,不弄了!要休息...
要抽到分类的代码
-(void)deciveCodeError:(NSNotification*)notification{
NSLog(@"%s",__func__);
NSString * msg = nil;
if([notification.object isEqualToString:@"001"]){
msg = @"你的账号已经在其他手机登录了,请注意是否本人操作";
[[QDNetworkManager share] canelAllRequests];
}
if([notification.object isEqualToString:@"002"]){
msg = @"你的账号已经在其他手机登录了,请注意是否本人操作";
[[QDNetworkManager share] canelAllRequests];
}
__weak typeof(self) weakSelf = self;
//判断最外层 VC是不是自己,是自己才弹
if (!self.deciveAlertView.isVisible&&![UCSInfo shareInfo].isShowingHasBeenKickOffNotificationAlertView) {
self.deciveAlertView=[[CXAlertView alloc]initWithTitle:nil message:msg cancelButtonTitle:@"重新登录"];
[self.deciveAlertView setWillDismissHandler:^(CXAlertView *alertView){
//清除用户的缓存
[[UCSInfo shareInfo] cleanAllValue];
if (![weakSelf isKindOfClass:[QDLoginContentVC class]]) {
[weakSelf jumpToLogin];
}
}];
[self.deciveAlertView show];
//记录正在显示 alertView,弹完框之后,默认不让手势密码显示了,需要这个来标记
[[UCSInfo shareInfo]setIsShowingHasBeenKickOffNotificationAlertView:YES];
}
}
写在最后
看看大牛的总结:
连类比事-category和extension
但是category则完全不一样,它是在运行期决议的。
就category和extension的区别来看,我们可以推导出一个明显的事实,extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。
参考文章
如果喜欢,帮我点个赞哦!