Fork me on GitHub

iOS开发之 ~ NSProxy浅说

概要

NSProxy是一个抽象的超类,它遵守了 NSObject 协议,并且是不继承自NSObject的。可以通过它的API为其它的Object对象或者不存在的对象提供替身。

.h头文件声明如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@class NSMethodSignature, NSInvocation;

NS_ROOT_CLASS
@interface NSProxy <NSObject> {
Class isa;
}

+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;

- (BOOL)allowsWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
- (BOOL)retainWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);

// - (id)forwardingTargetForSelector:(SEL)aSelector;

@end

NSProxy 的使用一般比较少,没了解之前看到它心里就冒出”这什么鬼,有点深奥呦!”的想法,其实非常简单,通常你只需要实现两个方法:

1
2
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");

那么我们通过Demo来给大家演示一下NSProxy的使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@interface MyProxy : NSProxy
{
id _object;
}

+ (id)proxyForObject:(id)obj;

@end

@implementation MyProxy

+ (id)proxyForObject:(id)obj {
MyProxy *instance = [MyProxy alloc];
instance->_object = obj;

return instance;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [_object methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
if ([_object respondsToSelector:invocation.selector]) {
NSString *selectorName = NSStringFromSelector(invocation.selector);

NSLog(@"Before calling \"%@\".", selectorName);
[invocation invokeWithTarget:_object];
NSLog(@"After calling \"%@\".", selectorName);

// 调用堆栈的符号
NSLog(@"%@", [NSThread callStackSymbols]);
}
}

@end

这是我们的 Proxy 简单实现,我们需要持有一个被代理对象的引用,然后将消息转发到这个对象上,在转发之前和以后我们就可以做自己想做的事情了。

methodSignatureForSelector: 方法需要获取一个方法签名,用来生成 NSInvocation,我们直接将这个调用转发到被代理对象中。紧接着,forwardInvocation: 会被调用,将 NSInvocation 用被代理对象调用。我们就可以在这个方法里做一些手脚,比如埋点计数等。在这个例子中,我只是简单地将对象所调用的方法的 selector 打印出来。

然后我们看看用于测试的主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

NSURL *url = [MyProxy proxyForObject:[NSURL URLWithString:@"https://www.baidu.com/"]];

NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {

}];

[task resume];

return YES;
}

就是简单构造一个 NSURL,只不过我们先用了 MyProxy 封装代理后传给 NSURLSession 去使用,输出结果如下:

1
2
2016-12-29 18:40:24.998735+0800 test[12184:851334] Before calling "absoluteURL".
2016-12-29 18:40:24.998814+0800 test[12184:851334] After calling "absoluteURL".

也就是说,系统用 NSURLabsoluteURL 属性来获取真正的 URL 数据,至此我们就已经可以跟踪已有类的行为了,甚至还可以通过 [NSThread callStackSymbols] 来跟踪调用改方法的函数调用栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2016-12-29 18:40:25.000404+0800 test[12184:851334] (
0 test 0x0000000102575108 -[MyProxy forwardInvocation:] + 296
1 CoreFoundation 0x00000001a3042e64 271BBB21-28D6-37CE-894F-43B3352DA6CE + 1162852
2 CoreFoundation 0x00000001a304509c _CF_forwarding_prep_0 + 92
3 CoreFoundation 0x00000001a3011758 CFURLCopyAbsoluteURL + 68
4 CFNetwork 0x00000001a361acb8 CFNetwork + 23736
5 CFNetwork 0x00000001a361ab80 CFNetwork + 23424
6 CFNetwork 0x00000001a361f7b4 CFNetwork + 42932
7 test 0x0000000102575b54 -[AppDelegate application:didFinishLaunchingWithOptions:] + 276
8 UIKitCore 0x00000001a588c9dc FBF3D986-2D16-3B6C-BE71-216144F6307F + 11676124
9 UIKitCore 0x00000001a588e97c FBF3D986-2D16-3B6C-BE71-216144F6307F + 11684220
10 UIKitCore 0x00000001a58940dc FBF3D986-2D16-3B6C-BE71-216144F6307F + 11706588
11 UIKitCore 0x00000001a4f6eef4 FBF3D986-2D16-3B6C-BE71-216144F6307F + 2117364
12 UIKitCore 0x00000001a589005c FBF3D986-2D16-3B6C-BE71-216144F6307F + 11690076
13 UIKitCore 0x00000001a5890464 FBF3D986-2D16-3B6C-BE71-216144F6307F + 11691108
14 UIKitCore 0x00000001a5895a08 UIApplicationMain + 164
15 test 0x0000000102574ecc main + 332
16 libdyld.dylib 0x00000001a2c99588 A333D9F4-8DA0-330D-B17E-0A92EC3DEF07 + 5512
)

并借此来跟踪一些系统行为。

iOS开发之 ~ NSTimer 循环引用

1. 概述

在使用NSTimer,如果使用不得当特别会引起循环引用,造成内存泄露。所以怎么避免循环引用问题,下面我提出几种解决NSTimer的几种循环引用。

在iOS中,NSTimer的使用是非常频繁的,但是NSTimer在使用中需要注意,避免循环引用的问题。之前经常这样写

1
2
3
4
5
6
7
8
- (void)setupTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}

- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
}

由于self强引用了timer,同时timer也强引用了self,所以循环引用造成dealloc方法根本不会走,self和timer都不会被释放,造成内存泄漏。

阅读更多...

iOS开发之 ~ 循环引用

1. 概述

iOS内存中的分区有:堆、栈、静态区。其中,栈和静态区是操作系统自己管理回收,不会造成循环引用。在堆中的相互引用无法回收,有可能造成循环引用。

  • 循环引用的实质:多个对象相互之间有强引用,不能施放让系统回收。

  • 解决循环引用一般是将 strong 引用改为 weak 引用。

阅读更多...

Git ~ 忽略已经提交过一次文件的骚操作

  • 已经推送(push)过的文件,想在以后的提交时忽略此文件,即使本地已经修改过,而且不删除git远程库中相应文件

  • 本地开发中需要用的文件,不能提交到远程,但是还不能写入.gitignore文件

1. 添加忽略命令

1
git update-index --assume-unchanged fileName

2. 取消对应文件的忽略

1
git update-index –no-assume-unchanged  fileName

3. 列出所有被忽略的文件(两个任意一个即可)

1
2
3
git ls-files -v | grep '^h\ '

git ls-files -v | grep '^h\ ' | awk '{print $2}'

4. 忽略一个目录

如果要忽略一个目录,打开 git bash,cd到 目标目录下,执行

1
git update-index --assume-unchanged $(git ls-files | tr '\n' ' ')

5. 取消所有被忽略的文件

1
git ls-files -v | grep '^h' | awk '{print $2}' |xargs git update-index --no-assume-unchanged

Mac ~ iTerm2 快捷操作

iTerm2是一款专为Mac OS X编写的,功能齐全的终端仿真程序,旨在为用户提供OS X下最佳的命令行经验。如果你能了解更多mac终端iTerm2快捷键,使用起来会更顺手。

官网: https://iterm2.com/

标签


  • 新建标签:command + t

  • 关闭标签:command + w

  • 切换标签:command + 数字 command + 左右方向键

  • 切换全屏:command + enter

  • 查找:command + f

    阅读更多...

SVN ~ Status字段含义小记

执行SVN up和svn merge等命令出现在首位置的各字母含义如下:

字符 含义
A 新增
C 冲突
D 删除
G 合并
I 忽略
M 改变
R 替换
X 未纳入版本控制,但被外部定义所用
? 未纳入版本控制
! 该项目已遗失 (被非 svn 命令所删除) 或是不完整
~ 版本控制下的项目与其它类型的项目重名
  • Copyrights © 2012-2022 Soto Pu

请我喝杯咖啡吧~

支付宝
微信