记iOS扩展(匿名类别)使用引发的崩溃

Posted WoodBear009

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记iOS扩展(匿名类别)使用引发的崩溃相关的知识,希望对你有一定的参考价值。

扩展(匿名类别)是在ios开发中经常用到的技巧,一般通常的用法是这样:

.h文件

@interface JH_WebViewController : JHBaseViewController
-(id)initWithUrl:(NSString *)url;
@end
.m文件

#import "JH_WebViewController.h"

@interface JH_WebViewController ()

    WKWebView *_webView;
    NSString *_url;

@end

@implementation JH_WebViewController
............

利用在.m文件中定义一个匿名扩展,将属性(方法)隐藏起来,使其不对外暴露,仅在对应的.m文件中可见(当然,其它文件硬去利用kvc也是可以访问到的,这个另说),达到文件(类)粒度的信息隐藏。

但是,我现在在做SDK,有下面这样的需求,有些类的属性(方法)想在我的整个项目(SDK)内是可见的,但对项目(SDK)外不可见(项目粒度的信息隐藏),比如下面这个类

@interface JHRoomMessage : JHBaseMessage

    


@property (nonatomic, copy) NSString *roomID;
@property (nonatomic, strong) JHRoomNof *roomNof;
@property (nonatomic, copy) NSString *atUserIds;

+(NSMutableArray *)getLastedMsgsByRoomId:(NSString *)roomId;
+(NSMutableArray *)getMsgsByRoomId:(NSString *)friendWorkId beforeMsg:(JHRoomMessage *)msg;

+(JHRoomMessage *)getLastedMsgByRoomId:(NSString *)roomId;

+(BOOL)saveSingleRoomMsg:(JHRoomMessage *)msg;

............
这个类中,roomNof属性还有很多方法都是给我SDK内部其它类去调用的,不想(不必)暴露给最终的SDK使用者,但如果我用一般定义匿名类别的方式,把匿名类别定义在.m文件中,就会导致这些属性(方法)在我的SDK内部也同样不可见了,我内部其它文件都无法去访问,于是我把定义方式换成了这样,新建一个JHRoomMessageEx类

JHRoomMessageEx.h

#import "JHRoomMessage.h"
#import "JHIMMsgExtendFieldUtil.h"

@interface JHRoomMessage()

@property (nonatomic, strong) JHRoomNof *roomNof;

+(BOOL)saveSingleRoomMsg:(JHRoomMessage *)msg;

+(void)resendRoomMsg:(JHRoomMessage *)msg originalId:(NSString *)originalId;

+(void)updateRoomMsgStatus:(JHRoomMessage *)msg;
.......
JHRoomMessage变成了这样(清爽多了)

#import <Foundation/Foundation.h>
#import "JHBaseMessage.h"

@interface JHRoomMessage : JHBaseMessage

@property (nonatomic, copy) NSString *roomID;
@property (nonatomic, copy) NSString *atUserIds;

+(NSMutableArray *)getLastedMsgsByRoomId:(NSString *)roomId;
+(NSMutableArray *)getMsgsByRoomId:(NSString *)friendUserId beforeMsg:(JHRoomMessage *)msg;
+(void)deletaMsg:(JHRoomMessage *)msg;
@end
这样问题就得到了解决,我项目内部其它文件引用JHRoomMessageEx.h就可以获知、使用扩展中的属性(方法),不受影响。而最终我在生成SDK库的时候,只会把JHRoomMessage.h文件暴露出去,这样JHRoomMessageEx中的信息就对SDK外部调用者不可见了,达到了SDK外隐藏的目的。

但这不是我今天要纪录的问题,

我在项目的另一个文件中引入了JHRoomMessageEx.h,并进行了如下调用

JHRoomMessage *roomMsg = [[JHRoomMessage alloc] init];
roomMsg.roomNof = roomNof;
......
结果,程序运行到这里时崩溃了,



没找到roomNof的set方法,why?头文件我引入了啊,编译也没问题啊,@property也写了啊.....

不过还好,静下心来一想,问题很快得到了解决。其实问题很简单,也很基础,但确实被忽视了

最终,我在JHRoomMessage.m文件中,#import "JHRoomMessageEx.h"就好了,原因很简单,

@property (nonatomic, strong) JHRoomNof *roomNof;
仅仅是相当于函数声明,具体的存取实现,需要在.m文件中由系统帮我们生成,而如果我不在JHRoomMessage.m中引入JHRoomMessageEx.h,JHRoomMessage就压根不会知道这个@property的存在,存取方法也就不会被合成,运行时也就找不到这个方法了。

其实在这个SDK项目中,我有很多别的类也都定义了对应的匿名类别.h文件,并在项目中使用,但类似的问题却一直也没有被暴露出来,总结一下,主要是以下几个原因:

1.一般一个类,自身的一个属性,大多也都会在自身的.m中用到,所以一般也就下意识的在.m文件中加入了Ex头文件的引入(不引入,编译也过不去),问题也就被掩盖了。而roomNof属性比较特殊,我在JHRoomMessage.m中没有去访问,这个属性纯粹是给别的类去赋值和使用的,所以我也就没在JHRoomMessage.m文件中去引入Ex头文件。

2.别的很多类对应的EX文件中,我大多情况下隐藏的都是一些自定义方法,或是单纯的属性声明。自己声明的自定义方法我肯定会去在.m里实现,要不声明它干啥。而属性声明,不涉及方法实现。@property本质上是属于函数声明,而它的具体实现,我又不用去操心,系统去做了,所以问题就暴露了,我没在.m中自己去写set(get),又没告知.m有@property的存在,结果就尴尬了。

3.文章开头那种普遍写法,问题不会暴露,匿名类别直接定义在.m中。

注:单纯的属性声明就是这个意思.....

@interface JHIMManager()

    dispatch_queue_t _joinRoomQueue;
    JHDataProxy *_dataProxy;
    NSMutableDictionary *_myRoomsDict;
    ........
这问题不大,也很基础,但感觉还是很有意思的,也确实容易被忽视,在此记录一下

以上是关于记iOS扩展(匿名类别)使用引发的崩溃的主要内容,如果未能解决你的问题,请参考以下文章

滚动表格视图时,iOS 应用程序随机崩溃,不确定如何处理引发的错误

UIImage+Resize 类别扩展错误和警告 iOS 7

UIImage+Resize 类别扩展错误和警告 iOS 7

从今天扩展程序打开时 iOS 应用程序崩溃

从可变参数函数的调用引发异常崩溃一例引发的一些思考

iOS - 类别和扩展(Categories和Extensions)