【iOSSharing #11】2022-01-05

181 阅读5分钟

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必须调用,如果不调用blockvc就不会置空,那么依旧是循环引用,selfblock都不会被释放.

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 ++;
    });
};