Swift值类型传递的问题

3,171 阅读4分钟

Swift相比Objective-C不止增加了很多特性,还有不少修改,对于实际编程中最容易出现的错误莫过于Swift将几乎所有Objective-C中对应的类型,都由引用类型都修改为值类型。

比如开发中最常用到的数据类型array:

NSMutableArray *array1 = [NSMutableArray arrayWithArray:@[@1, @2, @3]];
NSMutableArray *array2 = array1;
[array2 removeLastObject];
NSLog(@"%@", array1); // 1, 2
var array1 = [1, 2, 3]
var array2 = array1
array2.removeLast()
print(array1) // 1, 2, 3

刚开始学习Swift时,只是觉得这只是一个语言对于不同数据类型的设计不同而已,开发中只要注意对于需要函数内部修改的参数,加上inout即可。

但是随着对于Swift的深入使用,越来越觉得,这个设计相比于Objective-C的引用类型来说,确实是更加的难以使用。

比如实现一个对于数组的排序封装类,将数组传入并将数组按照升序排序,这里采用最简单的冒泡排序实现,如果是Objective-C实现:

#import <Foundation/Foundation.h>

@interface MySort<ObjectType>: NSObject 

- (void)sortWithArray:(NSMutableArray<ObjectType> *)array;

@end

@implementation MySort

- (void)sortWithArray:(NSMutableArray *)array {
	if (!array || array.count < 2) {
		return;
	}
	
	for (NSInteger end = array.count - 1; end > 0; end--) {
		for (NSInteger begin = 1; begin <= end; begin ++) {
			if ([array[begin] compare:array[begin - 1]] == NSOrderedAscending) {
				id tmp = array[begin];
				array[begin] = array[begin - 1];
				array[begin - 1] = tmp;
			}
		}
	}
}

@end

int main(int argc, char *argv[]) {
	NSMutableArray *array = [NSMutableArray arrayWithArray:@[@9, @8, @7, @6, @5, @4, @3, @2, @1, @0]];
	MySort *sort = [MySort new];
	[sort sortWithArray:array];
	NSLog(@"%@", array);
}

因为Objective-C中,数组是引用类型,直接将数组传递就可以实现对其排序,如果将上述的代码用Swift实现,需要在函数中修改array,需要将参数加上inout来实现:

public class MySort<ObjectType: Comparable> {
    public final func sortWithArray(array: inout [ObjectType]) {
        guard array.count > 1 else {
            return
        }
        for end in stride(from: array.count - 1, to: 0, by: -1) {
            for begin in 1 ... end {
                if array[begin] < array[begin - 1] {
                    let tmp = array[begin]
                    array[begin] = array[begin - 1]
                    array[begin - 1] = tmp
                }
            }
        }
    }
}

var array = [9,8,7,6,5,4,3,2,1,0]
var mySort = MySort<Int>()
mySort.sortWithArray(array: &array)
print(array)

目前来看没有任何问题,下面假如需要排序类先将数据保存,并在其它方法中实现排序。

在Objective-C中,只需要为类添加一个成员,并且将方法传入的数组对象传递给它即可:

@interface MySort<ObjectType>: NSObject {
@private
	NSMutableArray<ObjectType> *_array;
}

- (void)sortWithArray:(NSMutableArray<ObjectType> *)array;

@end

@implementation MySort

- (void)sortWithArray:(NSMutableArray *)array {
	if (!array || array.count < 2) {
		return;
	}
	
	_array = array;
}

- (void)sort {
	for (NSInteger end = _array.count - 1; end > 0; end--) {
		for (NSInteger begin = 1; begin <= end; begin ++) {
			if ([_array[begin] compare:_array[begin - 1]] == NSOrderedAscending) {
				id tmp = _array[begin];
				_array[begin] = _array[begin - 1];
				_array[begin - 1] = tmp;
			}
		}
	}
}

@end

但是Swift中就会出现问题:

public class MySort<ObjectType: Comparable> {
    private var array: [ObjectType]?
    
    public final func sortWithArray(array: inout [ObjectType]) {
        guard array.count > 1 else {
            return
        }
        self.array = array
        sort()
    }
    
    private func sort() {
        for end in stride(from: array!.count - 1, to: 0, by: -1) {
            for begin in 1 ... end {
                if array![begin] < array![begin - 1] {
                    let tmp = array![begin]
                    array![begin] = array![begin - 1]
                    array![begin - 1] = tmp
                }
            }
        }
    }
}

var array: [Int] = [9,8,7,6,5,4,3,2,1,0]
var mySort = MySort<Int>()
mySort.sortWithArray(array: &array)
// [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(array)

因为数组是值类型,将函数传入的数组传递给排序类的成员时:self.array = array 是一个默认拷贝的操作,在接下来的排序过程,只是对排序类中拷贝的数据进行排序。如果需要将函数传入的数组也修改排序,需要在sort()调用后,将本地拷贝的数组传递给外部的inout 参数:

    public final func sortWithArray(array: inout [ObjectType]) {
        guard array.count > 1 else {
            return
        }
        self.array = array
        sort()
        array = self.array!
    }

这样做非常的不好,因为平白无故的多出来两次数据的拷贝操作,最终我是使用了一个不太好的办法,就是利用指针来处理:

public class MySort<ObjectType: Comparable> {
    private var array: UnsafeMutablePointer<[ObjectType]>?
    
    public final func sortWithArray(array: inout [ObjectType]) {
        guard array.count > 1 else {
            return
        }
        self.array = withUnsafeMutablePointer(to: &array) { $0 }
        sort()
    }
    
    private func sort() {
        for end in stride(from: array!.pointee.count - 1, to: 0, by: -1) {
            for begin in 1 ... end {
                if array!.pointee[begin] < array!.pointee[begin - 1] {
                    let tmp = array!.pointee[begin]
                    array!.pointee[begin] = array!.pointee[begin - 1]
                    array!.pointee[begin - 1] = tmp
                }
            }
        }
    }
}

var array: [Int] = [9,8,7,6,5,4,3,2,1,0]
var mySort = MySort<Int>()
mySort.sortWithArray(array: &array)
print(array)

这样子确实避免了拷贝,不过真的非常的不好看,也不方便,只是解决了问题而已。

有人有更好的办法解决欢迎分享。