1.第一问:声明在.m顶部的宏是用来做什么的?
在浏览UIScrollView+YYAdd.m文件时,看见一个宏定义,好奇它是做什么的?往下看吧。
//问题路径:
YYKit/Base/UIKit/UIScrollView+YYAdd.m
//问题代码
#import "UIScrollView+YYAdd.h"
#import "YYKitMacro.h"
YYSYNTH_DUMMY_CLASS(UIScrollView_YYAdd)
@implementation UIScrollView (YYAdd)
...
@end
YYSYNTH_DUMMY_CLASS这个宏是做什么的?
点进去看看实现:
/**
Add this macro before each category implementation, so we don't have to use
-all_load or -force_load to load object files from static libraries that only
contain categories and no classes.
More info: http://developer.apple.com/library/mac/#qa/qa2006/qa1490.html .
*******************************************************************************
Example:
YYSYNTH_DUMMY_CLASS(NSString_YYAdd)
*/
#ifndef YYSYNTH_DUMMY_CLASS
#define YYSYNTH_DUMMY_CLASS(_name_) \
@interface YYSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
@implementation YYSYNTH_DUMMY_CLASS_ ## _name_ @end
#endif
在ios开发过程中,有时候会用到第三方的静态库(.a文件),OC没有为每个函数(或者方法)定义链接符号,它只为每个类创建链接符号。这样当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来,就会导致你调用类别中的方法时,会出现selector not recognized
的错误,从而导致app闪退。使用这段宏定义他可以虚拟新建一个与名字category 相同.h.m 让编译器 编译通过。即可解决上面的问题。
为什么会闪退?
Objective-C的链接器并不会为每个方法建立符号表,而是仅仅为类建立了符号表。这样的话,如果静态库中定义了已存在的一个类的分类,链接器就会以为这个类已经存在,不会把分类和核心类的代码合起来。这样的话,在最后的可执行文件中,就会缺少分类里的代码,这样函数调用就失败了。
接着仔细阅读库文件的说明文档,你可能会在文档中发现诸如在Other Linker Flags中加入-ObjC或者-all_load这样的解决方法。
看完这些解释,对这个宏一知半解,好奇的我又产生了新的问题:关于Xcode的Other Linker Flags
阅读这篇文章需要了解:链接器做什么
另外,在Ohter Linker Flags中的可使用命令行如下:
//加了这个参数后,链接器就会把静态库中所有的Objective-C类和分类都加载到最后的可执行文件中。
-ObjC
//会让链接器把所有找到的目标文件都加载到可执行文件中,但是千万不要随便使用这个参数!假如你使用了不止一个静态库文件,然后又使用了这个参数,那么你很有可能会遇到ld: duplicate symbol错误,因为不同的库文件里面可能会有相同的目标文件,所以建议在遇到-ObjC失效的情况下使用-force_load参数。
-all_load
//所做的事情跟-all_load其实是一样的,但是-force_load需要指定要进行全部加载的库文件的路径,这样的话,你就只是完全加载了一个库文件,不影响其余库文件的按需加载
-force_load
遗留问题:
$(inherited)
2.第二问:声明在.h的这个宏是干什么的?
同样是在浏览UIScrollView+YYAdd文件,这回是在.h文件中,看见一个宏定义,我这好奇心啊。
//问题路径:
YYKit/Base/UIKit/UIScrollView+YYAdd.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIScrollView (YYAdd)
...
@end
NS_ASSUME_NONNULL_END
NS_ASSUME_NONNULL_BEGIN
和NS_ASSUME_NONNULL_END
两个宏之间的代码,所有指针对象都被假定为nonnull, 即不能为空,否则编辑器会报警告Null passed to a callee that requires a non-null argument
。