学习Sunny博客的笔记。

Clang Attributes 是 Clang 提供的一种源码注解,方便开发者向编译器表达某种要求,参与控制如 Static Analyzer、Name Mangling、Code Generation 等过程,一般以__attribute__(xxx) 的形式出现在代码中;为方便使用,一些常用属性也被 Cocoa 定义成宏,比如在系统头文件中经常出现的NS_CLASS_AVAILABLE_IOS(9_0) 就是 __attribute__(availability(...)) 这个属性的简单写法。

常见属性的介绍,可以看 NSHipster 的介绍文章 和的 twitter 的介绍文章。本文还会介绍几个有意思的“黑魔法” Attribute,说不定在某些场景下会起到意想不到的效果哦~

以下测试都以 Xcode 7.3 ( Clang 3.8 ) 为准

objc_subclassing_restricted

使用这个属性可以定义一个 Final Class,也就是说,一个不可被继承的类,假设我们有个名叫 Eunuch(太监) 的类,但并不希望有人可以继承自它:

@interface Eunuch : NSObject
@end
@interface Child : Eunuch // 太监不能够有孩砸
@end

只要在 @interface 前面加上 objc_subclassing_restricted 这个属性即可:

__attribute__((objc_subclassing_restricted))
@interface Eunuch : NSObject
@end
@interface Child : Eunuch // <--- Compile Error
@end

objc_requires_super

aka: NS_REQUIRES_SUPER,标志子类继承这个方法时需要调用 super,否则给出编译警告:

@interface Father : NSObject
- (void)hailHydra __attribute__((objc_requires_super));
@end
@implementation Father
- (void)hailHydra {NSLog(@"hail hydra!");
}
@end
@interface Son : Father
@end
@implementation Son
- (void)hailHydra {
} // <--- Warning missing [super hailHydra]
@end

objc_boxable

Objective-C 中的 @(...) 语法糖可以将基本数据类型 box 成 NSNumber 对象,假如想 box 一个struct 类型或是 union 类型成 NSValue 对象,可以使用这个属性:

typedef struct __attribute__((objc_boxable)) {CGFloat x, y, width, height;
} XXRect;

这样一来,XXRect 就具备被 box 的能力:

CGRect rect1 = {1, 2, 3, 4};
NSValue *value1 = @(rect1); // <--- Compile Error
XXRect rect2 = {1, 2, 3, 4};
NSValue *value2 = @(rect2); // √

constructor / destructor

顾名思义,构造器和析构器,加上这两个属性的函数会在分别在可执行文件(或 shared library)loadunload 时被调用,可以理解为在 main() 函数调用前和 return 后执行:

__attribute__((constructor))
static void beforeMain(void) {NSLog(@"beforeMain");
}
__attribute__((destructor))
static void afterMain(void) {NSLog(@"afterMain");
}
int main(int argc, const char * argv[]) {NSLog(@"main");return 0;
}// Console:
// "beforeMain" -> "main" -> "afterMain"

constructor 和 +load 都是在 main 函数执行前调用,但 +load 比 constructor 更加早一丢丢,因为 dyld(动态链接器,程序的最初起点)在加载 image(可以理解成 Mach-O 文件)时会先通知objc runtime 去加载其中所有的类,每加载一个类时,它的 +load 随之调用,全部加载完成后,dyld 才会调用这个 image 中所有的 constructor 方法。

所以 constructor 是一个干坏事的绝佳时机:

  1. 所有 Class 都已经加载完成
  2. main 函数还未执行
  3. 无需像 +load 还得挂载在一个 Class 中

FDStackView 的 FDStackViewPatchEntry 方法便是使用的这个时机来实现偷天换日的伎俩。

PS:若有多个 constructor 且想控制优先级的话,可以写成 __attribute__((constructor(101))),里面的数字越小优先级越高,1 ~ 100 为系统保留。

enable_if

这个属性只能用在 C 函数上,可以用来实现参数的静态检查

static void printValidAge(int age)
__attribute__((enable_if(age > 0 && age < 120, "你丫火星人?"))) {printf("%d", age);
}

它表示调用这个函数时必须满足 age > 0 && age < 120 才被允许,于是乎:

printValidAge(26); // √
printValidAge(150); // <--- Compile Error
printValidAge(-1); // <--- Compile Error

cleanup

声明到一个变量上,当这个变量作用域结束时,调用指定的一个函数,Reactive Cocoa 用这个特性实现了神奇的 @onExit,关于这个 attribute,在之前的文章中有介绍,传送门。

overloadable

用于 C 函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型:

__attribute__((overloadable)) void logAnything(id obj) {NSLog(@"%@", obj);
}
__attribute__((overloadable)) void logAnything(int number) {NSLog(@"%@", @(number));
}
__attribute__((overloadable)) void logAnything(CGRect rect) {NSLog(@"%@", NSStringFromCGRect(rect));
}
// Tests
logAnything(@[@"1", @"2"]);
logAnything(233);
logAnything(CGRectMake(1, 2, 3, 4));

objc_runtime_name

用于 @interface@protocol,将类或协议的名字在编译时指定成另一个:

__attribute__((objc_runtime_name("SarkGay")))
@interface Sark : NSObject
@endNSLog(@"%@", NSStringFromClass([Sark class])); // "SarkGay"

所有直接使用这个类名的地方都会被替换(唯一要注意的是这时用反射就不对了),最简单粗暴的用处就是去做个类名混淆:

__attribute__((objc_runtime_name("40ea43d7629d01e4b8d6289a132482d0dd5df4fa")))
@interface SecretClass : NSObject
@end

还能用数字开头,怕不怕 - -,假如写个脚本把每个类前加个随机生成的 objc_runtime_name,岂不是最最精简版的代码混淆就完成了呢…

它是我所了解的唯一一个对 objc 运行时类结构有影响的 attribute,通过编码类名可以在编译时注入一些信息,被带到运行时之后,再反解出来,这就相当于开设了一条秘密通道,打通了写码时和运行时。脑洞一下,假如把这个 attribute 定义成宏,以annotation 的形式完成某些功能,比如:

// @singleton 包裹了 __attribute__((objc_runtime_name(...)))
// 将类名改名成 "SINGLETON_Sark_sharedInstance"
@singleton(Sark, sharedInstance)
@interface Sark : NSObject
+ (instancetype)sharedInstance;
@end

在运行时用 __attribute__((constructor)) 获取入口时机,用 runtime 找到这个类,反解出 “sharedInstance” 这个 selector 信息,动态将+ alloc- init 等方法替换,返回 + sharedInstance 单例。

References

http://llvm.org/releases/3.8.0/tools/clang/docs/AttributeReference.html
http://clang-analyzer.llvm.org/annotations.html

拷贝别人的一段话

通过编码类名可以在编译时注入一些信息,被带到运行时之后,再反解出来,这就相当于开设了一条秘密通道,打通了写码时和运行时。脑洞一下,假如把这个 attribute 定义成宏,以 annotation 的形式完成某些功能,比如:
// @singleton 包裹了 __attribute__((objc_runtime_name(...)))
// 将类名改名成 "SINGLETON_Sark_sharedInstance"
@singleton(Sark, sharedInstance)
@interface Sark : NSObject
+ (instancetype)sharedInstance;
@end
在运行时用 __attribute__((constructor)) 获取入口时机,用 runtime 找到这个类,反解出 “sharedInstance” 这个 selector 信息,动态将 + alloc,- init 等方法替换,返回 + sharedInstance 单例。

学习iOS 黑魔法 笔记相关推荐

  1. 0基础学习ios开发笔记第二天

    C语言的基本结构 c语言的入口函数是main函数. main函数的返回值行业标准是int return 数字:返回值 每条语句最后以分号结尾 注释:行注释.块注释 int main(void) {// ...

  2. ios学习--iphone开发笔记和技巧总结(原址持续更新)

    ios学习--iphone开发笔记和技巧总结(原址持续更新) 分类: ios Object-C2012-04-18 10:16 2716人阅读 评论(1) 收藏 举报 uiviewiphonelist ...

  3. [iOS]关于零基础学习iOS开发的学习方法总结

    关于零基础学习iOS开发的学习方法总结 最近很多零基础来参加蓝鸥培训的学生经常会问到一些学习方法的问题,就如下我自己见过的好的学习方法一起讨论一下. 蓝鸥iOS开发技术的学习路线图 程序员的主要工作是 ...

  4. iOS回顾笔记( 02 ) -- 由九宫格布局引发的一系列“惨案”

    iOS回顾笔记( 02 ) -- 由九宫格布局引发的一系列"惨案" 前言(扯几句淡先) 回顾到学习UI过程中的九宫格布局时,发现当时学的东西真是不少. 这个阶段最大的特点就是:知识 ...

  5. iOS回顾笔记( 01 )-- XIB和纯代码创建应用的对比

    iOS回顾笔记( 01 )--  XIB和纯代码创建应用的对比 很多时候我们工作很久突然闲下来的时候,是不是也感到无聊过?这就是我现在的生活,不过闲一段时间也挺好,可以好好回顾一下自己以前学习iOS路 ...

  6. IOS逆向笔记之HOOK实现(非越狱)

    HOOK是越狱的最终目标,目的是给应用添加功能如插件或者是更改应用的某个功能来满足我们的需求,如微信中添加抢红包插件.本文将以最近比较火的"快看"漫画为例子去除付费漫画中的收费弹窗 ...

  7. 零基础学习 iOS 开发

    作者:匿名用户 链接:https://www.zhihu.com/question/22000647/answer/114700565 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业 ...

  8. 乐鑫esp8266学习rtos3.0笔记第4篇:带你捋一捋微信公众号 airkiss 配网 esp8266 并绑定设备的过程,移植并成功实现在 esp8266 rtos3.1 sdk。(附带demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. Esp8266之 搭建开发环境,开始一个"hello ...

  9. 系统学习iOS动画之六:3D动画

    本文是我学习<iOS Animations by Tutorials> 笔记中的一篇. 文中详细代码都放在我的Github上 andyRon/LearniOSAnimations. 到目前 ...

最新文章

  1. select下拉option跳转页面
  2. 【OpenPose-Windows】中断问题及图像不同分辨率对帧率的影响
  3. 电脑常见的VGA、DVI、PS/2、USB等接口知识笔记,值得收藏!
  4. 【算法系列之十】三数之和
  5. 『号外』 排名进入3000,特致感谢!
  6. ios开发转行_做了几年嵌入式,想转服务器开发,该学什么?
  7. python中将str转成数字_python初探: 数据类型与变量
  8. 苹果macmac效率工具:Alfred
  9. Python之字符串格式化
  10. 【人脸识别】基于matlab GUI BP神经网络人脸识别(含识别率)【含Matlab源码 891期】
  11. DELL XPS M1530安装MAC OS X Lion 10.7.3经验分享!
  12. rufus中gpt和mrb磁盘_SSD固态硬盘用GPT还是MBR分区?
  13. 当潮流突破次元空间,你能想象吗?欢迎来到一个叫“人物动漫化”的程序
  14. nca算法_NCA告诉英国公民,立即寻找有史以来最恶劣的网络攻击的保护
  15. Windows桌面右键新建未出现word/excel/ppt解决办法
  16. 微软4000亿收购动视暴雪,背后逻辑是什么?
  17. HTML+CSS实现网易云音乐首页的主播电台页面
  18. 打印任何年月的日历表(Java)
  19. IOS 图标尺寸以及设备尺寸详解
  20. Linux的安装install

热门文章

  1. FYD-Focus Your Distribution-关注你的分布:异常检测和定位的从粗到细的非对比性学习-FYD
  2. JeeSite(JES)
  3. 华硕笔记本k555拆机图解_华硕(ASUS)K54HR笔记本拆机清灰图解
  4. 几款游戏引擎技术对比
  5. 我们应该如何看待牛市熊市?到了牛市熊市有什么标志吗?
  6. 全栈Python自动化测试学习资料【付费资源、看到即赚到,】
  7. 自己总结的一些spring面试题
  8. VBA操作Excel之获取单元格区域
  9. 【第六章】使用jQuery操作表单和表格2
  10. 3.25 使用钢笔工具选择平滑形状的叶子 [原创Ps教程]