IOS 开发高手课 学习笔记(第三部分)

本文介绍了iOS开发中除Cocoa Touch外的GUI框架,如WebKit、Flutter、Texture,并重点讲解了Texture的异步优势。此外,探讨了响应式框架 ReactiveCocoa 的应用与不足,以及如何利用Lottie实现酷炫的物理效果和过场动画。还提到了A/B测试、事件总线实现和提高JSON解析性能的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一部分戴铭老师说的是一些底层的东西,然后推荐了一下第三方库,但是看同学者的反应,很多内容(主要是推荐的第三方库)都不是很完善(不能完全接受),所以这部分内容,要谨慎对待,然后自己实践和查询足够的资料后,再选择接受相关内容。正所谓,尽信书不如无书,这部分内容最能反应这个观点。当然这不是否认戴老师的功劳,至少在眼界方面,学习之后得到了很大的扩宽!

Part 1. 除了 Cocoa,iOS还可以用哪些 GUI 框架开发?

在 iOS 开发时,默认使用的都是系统自带的 Cocoa Touch 框架,所以如果你还想进一步提高界面响应速度,赶超其他使用 Cocoa Touch 框架的 App 用户体验时,就要考虑使用其他的 GUI 框架来优化 App 界面的响应速度了。
现在流行的 GUI 框架除了 Cocoa Touch 外,还有 WebKit、Flutter、Texture(原名 AsyncDisplayKit)、Blink、Android GUI 等。其中,WebKit、Flutter、Texture 可以用于 iOS 开发。

Texture 框架,正是建立在 Cocoa Touch 框架之上的。Texture 和其他 GUI 框架一样都是使用的应用更加广泛的 FlexBox 布局。另外,Texture 是这些框架中唯一使用异步节点计算的框架。
基于以上三个方面的原因,如果要从 Cocoa Touch 框架前移到其他的 GUI 框架,从学习成本、收益等角度考虑的话,转到 Texture 会是个不错的选择。
Texture 最大的优势就是开发了线程安全的 ASDisplayNode,而且还能够很好的和 UIView 共生。

  • 实践,用Texture 实现UI

Part 2. 细说 iOS 响应式框架变迁,哪些思想可以为我所用?

说到 iOS 响应式框架,最开始被大家知道的是 ReactiveCocoa(简称 RAC),后来比较流行的是 RxSwift。但据我了解,iOS 原生开发使用 ReactiveCocoa 框架的团队并不多,而前端在推出 React.js 后,响应式思路遍地开花。那么,响应式框架到底是什么,为什么在 iOS 原生开发中没被广泛采用,却能在前端领域得到推广呢?
ReactiveCocoa 框架并没有为 iOS 的 App 带来更好的性能。当一个框架可有可无,而且没有明显收益时,一般团队是没有理由去使用的。

从本质上看,响应式编程没能提高 App 的性能,是其没能流行起来的主要原因。在调试上,由于 ReactiveCocoa 框架采用了 Monad 模式,导致其底层实现过于复杂,从而在方法调用堆栈里很难去定位到问题。这,也是 ReactiveCocoa 没能流行起来的一个原因。但, ReactiveCocoa 的上层接口设计思想,可以用来提高代码维护的效率,还是可以引入到 iOS 开发中的(DEMO:https://github.com/ming1016/RACStudy.git)。ReactiveCocoa 里面还有很多值得我们学习的地方,比如说宏的运用
对于 iOS 开发来说,响应式编程还有一个很重要的技术是 KVO,使用 KVO 来实现响应式开发的范例可以参考戴老师以前的一个 demo

Part 3. 如何构造酷炫的物理效果和过场动画效果?

Lottie
Lottie 框架就很好地解决了动画制作与开发隔离,以及多平台统一的问题。
动画设计师做好动画以后,可以使用After Effects将动画导出成 JSON 文件,然后由 Lottie 加载和渲染这个 JSON 文件,并转换成对应的动画代码。由于是 JSON 格式,文件也会很小,可以减少 App 包大小。运行时还可以通过代码控制更改动画,比如更改颜色、位置以及任何关键值。另外,Lottie 还支持页面切换的过场动画(UIViewController Transitions)。

Bodymovin
你需要先到Adobe 官网下载 Bodymovin 插件,并在 After Effects 中安装。使用 After Effects 制作完动画后,选择 Windows 菜单,找到 Extensions 的 Bodymovin 项,在菜单中选择 Render 按钮就可以输出 JSON 文件了。

在 iOS 中使用 Lottie
在 iOS 开发中使用 Lottie 也很简单,只要集成 Lottie 框架,然后在程序中通过 Lottie 的接口控制 After Effects 生成的动画 JSON 就行了。首先,你可以通过 CocoaPods 集成 Lottie 框架到你工程中。Lottie iOS 框架的 GitHub 地址是https://github.com/airbnb/lottie-ios/,官方也提供了可供学习的示例。然后,快速读取一个由 Bodymovin 生成的 JSON 文件进行播放。具体代码如下所示:

pod 'lottie-ios', '=2.5.2'

- (void)testLottieAnimation{
    aniView = [LOTAnimationView animationNamed:@"lf20_0y27xv96"];
    aniView.loopAnimation = YES;
    [self.view addSubview:aniView];
    [aniView play];
}

备注:免费的动画json,可以从lottiefiles这里下载

利用 Lottie 的动画进度控制能力,还可以完成手势与动效同步的问题。动画进度控制是 LOTAnimationView 的 animationProgress 属性,设置属性的示例代码如下:

CGPoint translation = [gesture getTranslationInView:self.view];
CGFloat progress = translation.y / self.view.bounds.size.height;
animationView.animationProgress = progress;

Lottie 还带有一个 UIViewController animation-controller
可以自定义页面切换的过场动画,示例代码如下:


#pragma mark -- 定制转场动画

// 代理返回推出控制器的动画
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
  LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition1" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
  return animationController;
}

// 代理返回退出控制器的动画
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
  LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition2" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
  return animationController;
}

多平台支持
Lottie 支持多平台,除了 支持iOS,还支持 Android 、React Native和Flutter。除了官方维护的这些平台外,Lottie 还支持Windows、Qt、Skia 。陈卿还实现了 React、Vue和Angular对 Lottie 的支持,并已将代码放到了 GitHub 上。
官方教程:http://airbnb.io/lottie/#/ios

Lottie 实现原理,自行搜索。

Part 4. A/B 测试:验证决策效果的利器

A/B测试大讨论
开源SDK – 可以学着写自己的A/B测试SDK
Google的A/B的测试文档

Part 5. 怎样构建底层的发布和订阅事件总线?

由于 Delegate 和 Block 只适合做一对一数据传递,KVO 和 NSNotificationCenter 虽然可以支持一对多的数据传递,但存在过于灵活而无法管控和维护的问题,而事件总线需要通过发布和订阅这种可管控方式实现一对一和一对多数据传递。由此可以看出,iOS 现有的 Delegate、Block、KVO、NSNotificationCenter 等技术并不适合来做事件总线。
响应式第三方库 ReactiveCocoa 和 RxSwift 对事件总线的支持是没有问题的,但这两个库更侧重的是响应式编程,事件总线只是其中很小的一部分。所以,使用它们的话,就有种杀鸡焉用牛刀的感觉。那么,事件总线有没有小而美的第三方库可用呢?
前端Promise –>PromiseKit

PromiseKit(是个好东西)
我们先来看看如何使用 Promise 对象的 then 和 catch 方法。假设有这么一个需求:首先,通过一个异步请求获取当前用户信息;然后,根据获取到的用户信息里的用户编号再去异步请求获取用户的时间轴列表;最后,将用户的时间轴列表数据,赋值给当前类的时间轴列表属性。这里,我先给出使用 PromiseKit 实现的具体代码,然后我再和你分析其中的关键步骤。使用 PromiseKit 实现的代码如下:


firstly {
    // 异步获取当前用户信息
    fetchUserInfo()
}.then { userInfo in
    // 使用异步获取到的用户信息中的 uid 再去异步获取用户的 timeline
    fetchUserTimeline(uid: userInfo.uid)
}.then { timeline in
    // 记录 timeline
    self.timeline = timeline
}.catch {
    // 整个方法链的错误都会在这处理
}

除了 then 和 catch 方法以外,PromiseKit 还有一些好用的方法。比如 always 方法。使用了 always 方法以后, Promise 对象每次在执行方法时,都会执行一次 always 方法。再比如 when 方法。这个方法的使用场景就是,指定多个异步操作,等这些操作都执行完成后就会执行 when 方法。when 方法类似 GCD 里面的 Dispatch Group,虽然实现的功能一样,但是代码简单了很多,使用起来也更加方便。
PromiseKit 还为苹果的 API 提供了扩展。这些扩展需要单独集成,你可以在PromiseKit 组织页面获取。
https://github.com/PromiseKit

  • 实践:pod下载PromiseKit一直没下载成功,后续补充实践代码

Part 6. 如何提高 JSON 解析的性能?

系统自带的JSONDecoder 如何解析 JSON?J
在 decode 方法里可以看到,对于传入的 Data 数据会首先通过 JSONSerialization 方法转化成 topLevel 原生对象,然后 topLevel 原生对象通过 JSONDecoder 初始化成一个 JSONDecoder 对象,最后使用 JSONDecoder 的 unbox 方法将数据和传入的结构体对应上,并保存在结构体里进行返回。
提高 JSON 解析性能
2019 年 2 月,Geoff Langdale 和 Daniel Lemire 发布了 simdjson。 simdjson 是一款他们研究了很久的快速 JSON 解析器, 号称每秒可解析千兆字节 JSON 文件。具体的使用代码如下:


#include "simdjson/jsonparser.h"

/...

const char * filename = ... // JSON 文件
std::string_view p = get_corpus(filename);
ParsedJson pj = build_parsed_json(p); // 解析方法
// you no longer need p at this point, can do aligned_free((void*)p.data())
if( ! pj.isValid() ) {
    // 出错处理
}
aligned_free((void*)p.data());

备注1:博主补充,swift下对应的库,应该是pod ‘ZippyJSON’,其使用方法跟系统内置的一样,只是class名改为ZippyJSONDecoder,其它接口名称跟系统一致。
备注2:备注:目前simdjson貌似还不支持arm64架构 所以无法在iOS真机设备上运行 只能在x86-64上运行,而且很多同学反馈,实践使用时simdjson的效率跟系统自带的差不多,没有说的那么强(也可能是使用方法不对)

Part 7. 如何用 Flexbox 思路开发?跟自动布局比,Flexbox 好在哪?

Flexbox 好在哪?
目前来看,iOS 系统提供的布局方式有两种:一种是 Frame 这种原始方式,也就是通过设置横纵坐标和宽高来确定布局。另一种是自动布局(Auto Layout),相比较于 Frame 需要指出每个视图的精确位置,自动布局对于视图位置的描述更加简洁和易读,只需要确定两个视图之间的关系就能够确定布局。通过 Masonry和 SnapKit这些第三方库,自动布局的易用性也有了很大提升。
那么在这种情况下,我们为什么还要关注其他布局思路呢?关于原因,我觉得主要包括以下两个方面。其一,自动布局思路本身还可以再提高。其二,针对多个平台的库需要使用更加通用的布局思想。

Texture 如何使用 Flexbox 思路进行布局?

基于 Flexbox 的布局思路,Texture 框架的布局方案考虑得十分长远,也已经十分成熟,虽然学习起来需要费些力气,但是性能远好于苹果的自动布局,而且写起来更简单。Texture 框架的布局中,Texture 考虑到布局扩展性,提供了一个基类 ASLayoutSpec。这个基类 提供了布局的基本能力,使 Texture 可以通过它扩展实现多种布局思路,比如 Wrapper、Inset、Overlay、Ratio、Relative、Absolute 等布局思路,也可以继承 ASLayoutSpec 来自定义你的布局算法。

  • 实践1:集成yoga,实现Flex布局
#import <YogaKit/UIView+Yoga.h>
-(void)testYoga{
    [self.view configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
        layout.isEnabled = YES;
        layout.alignContent = YGAlignCenter;
        layout.alignItems = YGAlignSpaceAround;
    }];
    __block YGValue value;
    value.unit = YGUnitPoint;
    value.value = 200.0f;
    [aniView configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
        layout.isEnabled = YES;
        layout.height = value;
        layout.width = value;
        value.value = 40.0f;
        layout.marginStart = value;
    }];
    [aniView.yoga applyLayoutPreservingOrigin:YES];
}
  • 实践2:基于 ASLayoutElement 协议,实现一个 Texture 自定义布局类

备注:这是戴老师第三次提到Textture了,所以一定要看看

Part 8. 怎么应对各种富文本表现需求?
WebView
使用 WebView 显示文章只需要创建一个 UIWebView 对象,进行一些基本滚动相关的设置,然后读取 HTML 字符串就可以了,具体实现代码如下:


self.wbView = [[UIWebView alloc] init];
self.wbView.delegate = self;
[self.view addSubview:self.wbView];
[self.wbView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.left.right.bottom.equalTo(self.view);
}];
self.wbView.scalesPageToFit = YES; // 确保网页的显示尺寸和屏幕大小相同
self.wbView.scrollView.directionalLockEnabled = YES; // 只在一个方向滚动
self.wbView.scrollView.showsHorizontalScrollIndicator = NO; // 不显示左右滑动
[self.wbView setOpaque:NO]; // 默认是透明的

// 读取文章 html 字符串进行展示
[self.wbView loadHTMLString:articleString baseURL:nil];

和 UIWebView 的 loadRequest 相比,UIWebView 通过 loadHTMLString 直接读取 HTML 代码,省去了网络请求的时间,展示的速度非常快。不过,HTML 里的图片资源还是需要通过网络请求来获取。所以,如果能够在文章展示之前就缓存下图片,那么无需等待,就能够快速完整地展示丰富的文章内容了。
在 Cocoa 层使用 NSURLProtocol 可以拦截所有 HTTP 的请求,因此我可以利用 NSURLProtocol 来缓存文章中的图片。

在长列表这种场景下,如果不用 HTML 来描述富文本的话,想要使用原生 iOS 代码来描述富文本的话,你还可以使用苹果官方的TextKit和 YYText来展示。
YYText集成 YYText 到你的 App 非常简单,只需要在 Podfile 中添加 pod ‘YYText’ 就可以了。下面代码展示了如何展示图文混排的富文本:


NSMutableAttributedString *text = [NSMutableAttributedString new];
UIFont *font = [UIFont systemFontOfSize:16];
NSMutableAttributedString *attachment = nil;
  
// 嵌入 UIImage
UIImage *image = [UIImage imageNamed:@"dribbble64_imageio"];
attachment = [NSMutableAttributedString yy_attachmentStringWithContent:image contentMode:UIViewContentModeCenter attachmentSize:image.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
[text appendAttributedString: attachment];
  
// 嵌入 UIView
UISwitch *switcher = [UISwitch new];
[switcher sizeToFit];
attachment = [NSMutableAttributedString yy_attachmentStringWithContent:switcher contentMode:UIViewContentModeBottom attachmentSize:switcher.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
[text appendAttributedString: attachment];
  
// 嵌入 CALayer
CASharpLayer *layer = [CASharpLayer layer];
layer.path = ...
attachment = [NSMutableAttributedString yy_attachmentStringWithContent:layer contentMode:UIViewContentModeBottom attachmentSize:switcher.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
[text appendAttributedString: attachment];

通过上面 YYText 描述富文本的代码,你会发现原生代码描述富文本跟 HTML 比,既复杂又啰嗦。HTML 代码更易读、更容易维护,所以除了长列表外,我建议你都使用 HTML 来描述富文本。对于 UIWebView 内存占用高的问题,你可以考虑使用 HTML 代码转原生代码的思路解决。

Part 9. 如何在 iOS 中进行面向测试驱动开发和面向行为驱动开发?

从开发模式划分的话,开发方式可以分为 TDD(Test-driven development,面向测试驱动开发)和 BDD(Behavior-driven development ,面向行为驱动开发)。
TDD 的开发思路是,先编写测试用例,然后在不考虑代码优化的情况下快速编写功能实现代码,等功能开发完成后,在测试用例的保障下,再进行代码重构,以提高代码质量。
BDD 是 TDD 的进化,基于行为进行功能测试,使用 DSL(Domain Specific Language,领域特定语言)来描述测试用例,让测试用例看起来和文档一样,更易读、更好维护。
BDD相比 TDD,BDD 更关注的是行为方式的设计,通过对行为的描述来验证功能的可用性。行为描述使用的 DSL,规范、标准而且可读性高,可以当作文档来使用。
BDD 的 Objective-C 框架有 Kiwi、Specta、Expecta等,Swift 框架有 Quick。Kiwi 框架不光有 Specta 的 DSL 模式,Expecta 框架的期望语法,还有 Mocks 和 Stubs 这样的模拟存根能力。所以接下来,我就跟你说说这个 iOS 中非常有名并且好用的 BDD 框架,以及怎么用它来进行 BDD 开发。
将 Kiwi 集成到你的 App 里,只需要在 Podfile 里添加 pod ‘Kiwi’ 即可。下面这段代码,是 Kiwi 的使用示例:


// describe 表示要测试的对象
describe(@"RSSListViewController", ^{
    // context 表示的是不同场景下的行为
    context(@"when get RSS data", ^{
        // 同一个 context 下每个 it 调用之前会调用一次 beforeEach
        beforeEach(^{
            id dataStore = [DataStore new];
        });


        // it 表示测试内容,一个 context 可以有多个 it
        it(@"load data", ^{
            // Kiwi 使用链式调用,should 表示一个期待,用来验证对象行为是否满足期望
            [[theValue(dataStore.count) shouldNot] beNil];
        });
    });
});

编码规范 && 学习资料

不作笔记 网上很多

Part 10. iOS 系统内核 XNU:App 如何加载?

XNU 加载就是为 Mach-O 创建一个新进程,建立虚拟内存空间,解析 Mach-O 文件,最后映射到内存空间。流程可以概括为:
fork 新进程;
为 Mach-O 分配内存;
解析 Mach-O;
读取 Mach-O 头信息;
遍历 load command 信息,将 Mach-O 映射到内存;
启动 dyld。

Part 11. iOS 黑魔法 Runtime Method Swizzling 背后的原理

直接使用 Runtime 方法交换开发的风险有哪些?
你可以通过于德志 (@halfrost) 博客的三篇 Runtime 文章,即isa 和 Class、消息发送与转发,以及如何正确使用 Runtime,来一边学习一边调试。
RSSwizzle库里指出了四个典型的直接使用 Runtime 方法进行方法交换的风险。我稍作整理,以方便你查看,并便于你理解后续的内容。
第一个风险是,需要在 +load 方法中进行方法交换。因为如果在其他时候进行方法交换,难以保证另外一个线程中不会同时调用被交换的方法,从而导致程序不能按预期执行。
第二个风险是,被交换的方法必须是当前类的方法,不能是父类的方法,直接把父类的实现拷贝过来不会起作用。父类的方法必须在调用的时候使用,而不是方法交换时使用。
第三个风险是,交换的方法如果依赖了 cmd,那么交换后,如果 cmd 发生了变化,就会出现各种奇怪问题,而且这些问题还很难排查。特别是交换了系统方法,你无法保证系统方法内部是否依赖了 cmd。
第四个风险是,方法交换命名冲突。如果出现冲突,可能会导致方法交换失败。

更安全的方法交换库 Aspects
Aspects 的整体流程是,先判断是否可进行方法交换。这一步会进行安全问题的判断处理。如果没有风险的话,再针对要交换的是类对象还是实例对象分别进行处理。
对于类对象的方法交换,会先修改类的 forwardInvocation ,将类的实现转成自己的。然后,重新生成一个方法用来交换。最后,交换方法的 IMP,方法调用时就会直接对交换方法进行消息转发。
对于实例对象的方法交换,会先创建一个新的类,并将当前实例对象的 isa 指针指向新创建的类,然后再修改类的方法。
如何使用 Aspects 的示例代码:

[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) {
    NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated);
} error:NULL];

Part 12. libffi:动态调用和定义 C 函数

在 iOS 开发中,我们可以使用 Runtime 接口动态地调用 Objective-C 方法,但是却无法动态调用 C 的函数。那么,我们怎么才能动态地调用 C 语言函数呢?
不同的 CPU 架构,在编译时会执行不同的 objc_msgSend 函数,而且 objc_msgSend 函数无法直接调用 C 函数,所以想要实现动态地调用 C 函数就需要使用另一个用汇编语言编写的库 libffi。那么,libffi 是什么呢,又怎么使用 libffi 来动态地调用 C 函数?
libffi 通过调用 ffi_call(函数调用) 来进行函数调用,ffi_call 的输入是 ffi_cif(模板)、函数指针、参数地址。其中,ffi_cif 由 ffi_type(参数类型) 和 参数个数生成,也可以是 ffi_closure(闭包)。
具体使用例子,可参考github Demo:https://github.com/sunnyxx/libffi-iOS.git

#import <ffi.h>
#import <objc/runtime.h>

@interface Sark : NSObject
@end

@implementation Sark

- (int)fooWithBar:(int)bar baz:(int)baz {
    return bar + baz;
}

@end

void testFFICall() {
    ffi_cif cif;
    ffi_type *argumentTypes[] = {&ffi_type_pointer, &ffi_type_pointer, &ffi_type_sint32, &ffi_type_sint32};
    ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 4, &ffi_type_pointer, argumentTypes);
    
    Sark *sark = [Sark new];
    SEL selector = @selector(fooWithBar:baz:);
    int bar = 123;
    int baz = 456;
    void *arguments[] = {&sark, &selector, &bar, &baz};
    
    IMP imp = [sark methodForSelector:selector];
    
    int retValue;
    ffi_call(&cif, imp, &retValue, arguments);
    NSLog(@"ffi_call: %d", retValue);
}

void closureCalled(ffi_cif *cif, void *ret, void **args, void *userdata) {
    int bar = *((int *)args[2]);
    int baz = *((int *)args[3]);
    *((int *)ret) = bar * baz;
}

void testFFIClosure() {
    ffi_cif cif;
    ffi_type *argumentTypes[] = {&ffi_type_pointer, &ffi_type_pointer, &ffi_type_sint32, &ffi_type_sint32};
    ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 4, &ffi_type_pointer, argumentTypes);
    IMP newIMP;
    ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), (void *)&newIMP);
    ffi_prep_closure_loc(closure, &cif, closureCalled, NULL, NULL);
    
    Method method = class_getInstanceMethod([Sark class], @selector(fooWithBar:baz:));
    method_setImplementation(method, newIMP);
    
    // after hook
    Sark *sark = [Sark new];
    int ret = [sark fooWithBar:123 baz:456];
    NSLog(@"ffi_closure: %d", ret);
}

// 函数指针 fn IMP imp = [sark methodForSelector:selector];
在这个例子中,函数指针是使用 Objective-C 的 Runtime 得到的。如果是 C 语言函数,你就可以通过 dlsym 函数获得。dlsym 获得函数指针示例如下:


// 计算矩形面积
int rectangleArea(int length, int width) {
    printf("Rectangle length is %d, and with is %d, so area is %d \n", length, width, length * width);
    return length * width;
}

void run() {
    // dlsym 返回 rectangleArea 函数指针
    void *dlsymFuncPtr = dlsym(RTLD_DEFAULT, "rectangleArea");
}
  • 实践:了解block的定义以及通过 libffi 调用 Block

iOS 是怎么管理内存的?&& 如何编写 Clang 插件?

搜之,网上资料多~

未完待续,最后一部分,戴老师主要讲前端相关~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值