iOS Sharing 系列
目录
1. Swift结构体和类的区别有哪些?
2. iOS进程间通信(IPC)方式有哪些?
3. iOS线程间通信(ITC)方式有哪些?
4. 解除Block循环引用的方式有哪些?
1. Swift结构体和类的区别有哪些?
- 1). 从内存分配来讲,结构体一般在栈区,类一般在堆区
- 2). 对于结构体及其成员的操作都是“深拷贝”,并且伴有 cow(写入时拷贝) 技术,所以理论上结构体性能更好(copy on write:写入时复制,或者拷贝,简单讲拷贝一个结构体,它并不会立即拷贝一份,而是标记一下,只有在你真正要修改拷贝的时候,才发生拷贝动作,目的就是为了性能,这个是中途增加的,swift 刚开始没有)
- 3). 结构体由于是值类型,不存在引用计数,它的生命周期由操作系统直接管理,类是引用类型,它有引用计数,它的生命周期由 arc 管理
- 4). 结构体不存在继承,类有继承,开发中如果一个功能可以不用继承来实现,优先考虑结构体实现功能
- 5). 有些协议限制了只能是类遵循,结构体等值类型无法遵循
- 6). 结构体因为一创建就确定了,正常情况下不能修改,如需要需用关键字 mutating
2. iOS进程间通信(IPC)方式有哪些?
1). URL Scheme
通过openURL的方法跳转且可以在URL中带上参数。 源APP A在info.plist中配置LSApplicationQueriesSchemes,指定目标App B的scheme,然后在目标App B的info.plist 中配置好URL types,表示该App接受何种URL Scheme的唤起。
2). Keychain
keychain是iOS的一个安全存储容器,它本质上就是一个sqlite数据库,它的位置存储在/private/var/Keychains/keychain-2.db
,不过它的所有数据都是经过加密的,可以用来为不同的APP保存敏感信息,比如用户名,密码等。iOS系统自己也用keychain来保存VPN凭证和WiFi密码。它是独立于每个APP的沙盒之外的,即使APP被删除之后,keychain里面的信息依然存在。
3). UIPasteBoard
系统剪切板
4). UIDocumentInteractionController
主要是用来实现同设备上APP之间的共享文档,以及文档预览、打印、发邮件和复制等功能
5). UIActivityViewController
APP之间发送分享数据
6). APP Groups
APP group用于同一个开发团队开发的APP之间,包括APP和extension之间(CFNotificationCenterRef)共享同一份读写空间,进行数据共享。
7). Local socket
APP A在本地的端口port:1234 进行TCP的bind 和 listen,另外一个APP B在同一个端口port:1234发起TCP的connect连接,这样就可以建立TCP连接,进行通信了
8). AirDrop
实现不同设备的APP之间文档和数据分享
3. iOS线程间通信(ITC)方式有哪些?
1). performSelector:onThread
指定在某个线程调用(耗时操作至于非主线程)
[self performSelector:@selector(downloadData) onThread:background_thread withObject:nil waitUntilDone:YES];
指定在背景线程执行操作
[self performSelectorInBackground:@selector(downloadData) withObject:nil];
在主线程调用
- (void)downloadData {
//执行download操作
...
// 回到主线程
[self performSelectorOnMainThread:@selector(finishDownload:) withObject:data waitUntilDone:YES];
}
- (void)finishDownload:(NSData *)data {
// 刷新UI
...
}
2). NSMachPort
能使用这种方式实现的,都可以用方式 1)替代
@implementation ViewController {
NSThread *_thread1;
NSThread *_thread2;
NSMachPort *_port1;
NSMachPort *_port2;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initData];
}
- (void)initData {
_thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(startRunloop1) object:nil];
_thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(startRunloop2) object:nil];
_port1 = (NSMachPort *)[NSMachPort port];//返回的是NSPort
_port2 = (NSMachPort *)[NSMachPort port];
_port2.delegate = self;
[_thread1 start];
[_thread2 start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self invokeITC];
}
- (void)startRunloop1 {
[[NSRunLoop currentRunLoop] addPort:_port1 forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)startRunloop2 {
[[NSRunLoop currentRunLoop] addPort:_port2 forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)sendData {
NSArray *array = @[@"send", @"data"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:NO error:nil];
NSArray *dataArray = @[data];
//_thread1用_port1往_thread2发消息
//注意components 数组里的数据必须是NSData或NSPort类型
[_port2 sendBeforeDate:[NSDate date] msgid:123456 components:dataArray.mutableCopy from:_port1 reserved:0];
}
- (void)invokeITC {
[self performSelector:@selector(sendData) onThread:_thread1 withObject:nil waitUntilDone:YES];
}
/**
这里注意实现的是 NSPortDelegate `- (void)handlePortMessage:(NSPortMessage *)message;` 的方法,
而不是 NSMachPortDelegate `- (void)handleMachMessage:(void *)msg;`
NSMachPortDelegate 的方法参数是不透明结构 void *,所以调用了 NSPortDelegate 的方法,
但是由于 NSPortMessage * 也是不透明的,看不到类内部定义,且无法使用 valueForKey方法,
所以将 NSPortMessage * 改成了 id
*/
- (void)handlePortMessage:(id)message{
NSLog(@"%@",message);
NSMutableArray *dataArray = [message valueForKey:@"components"];
NSMutableArray *array = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:dataArray.lastObject error:nil];
NSLog(@"%@",array);
}
3). GCD
dispatch_async(dispatch_get_main_queue(), ^{
[self sendMessage];
});
4. 解除Block循环引用的方式有哪些?
1). 使用weak修饰
a). 被引用对象生命周期与block内部代码一致
使用weak即可
__weak typeof(self) weakSelf = self;
self.block = ^(void) {
weakSelf.age ++;
};
b). 被引用对象生命周期可能比block内部代码(延时执行之类的)更早结束
为防止被引用对象提前销毁,block内部需引用一次该对象
__weak typeof(self) weakSelf = self;
self.block = ^(void) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//做一次引用,此时被引用对象生命周期结束(例如 pop viewController),
//但是这里还有引用,被引用对象不会释放
__strong typeof(self) strongSelf = weakSelf;
strongSelf.age ++;
});
};
2). 使用__block修饰
使用block修饰,拷贝一份被引用对象,需手动释放该拷贝
__block ViewController *vc = self;
self.block = ^(void) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
vc.age ++;
vc = nil;//手动释放该拷贝
});
};
需要注意的是这里的block
必须调用,如果不调用block
,vc
就不会置空,那么依旧是循环引用,self
和block
都不会被释放.
3). 作为参数传入
参数是栈变量,会自动释放
self.block = ^(ViewController *vc) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
vc.age ++;
});
};
4). 使用中介断开循环引用
a). 使用NSProxy
@interface MyProxy ()
@property (nonatomic, weak, readonly) NSObject *object;
@end
@implementation MyProxy
- (id)transformObject:(NSObject *)object{
_object = object;
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
NSMethodSignature *signature;
if (self.object) {
signature = [self.object methodSignatureForSelector:sel];
} else {
signature = [super methodSignatureForSelector:sel];
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = [invocation selector];
if ([self.object respondsToSelector:sel]) {
[invocation invokeWithTarget:self.object];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector{
return [self.object respondsToSelector:aSelector];
}
@end
b). 使用自己的类将被引用对象当成一个被weak修饰的属性
@interface MyTest: NSObject
@property (nonatomic, weak) ViewController *vc;
@end
使用
MyTest *test = [MyTest new];
test.vc = self;
self.block = ^(void) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
test.vc.age ++;
});
};