1, Foreword
As mentioned above, under the OC/Swift mix, the following suggestions are made for the data types of attributes or parameters:
- 1. Use immutable types as much as possible and immutable types as little as possible. (suggestions: NSString *, NSArray *, - NSDictionary *)
- 2. Use generics as much as possible to specify the type of elements in immutable types. (suggestion: nsdictionary < nsstring *, ID >)
- 3. As far as possible, do not use its pointer (Bool *) for value type, and as far as possible, do not use its secondary pointer for pointer type. (NSString**)
This article will explain why this is done through a simple example.
2, Code structure
Suppose there is an OC model class as follows:
@interface TestDataModel : NSObject @property (nonatomic, strong) NSDictionary *dict1; @property (nonatomic, strong) NSMutableDictionary *dict2; @property (nonatomic, strong) NSDictionary<NSString*,id> *dict3; @property (nonatomic, strong) NSMutableDictionary<NSString*,id> *dict4; @property (nonatomic, strong) NSDictionary *errorTypeDict; @property (nonatomic, assign) BOOL boolValue; @property (nonatomic, assign) BOOL *boolPtrValue; -(void)printBool:(BOOL)boolValue; -(void)printBoolPtr:(BOOL*)boolPtrValue; @end @implementation TestDataModel -(instancetype)init { if( self=[super init] ) { self.dict1 = @{ @"strKey":@"strValue", @"boolKey":@(YES), @"dictKey":@{@"key":@"value"} }; self.dict2 = [NSMutableDictionary dictionaryWithDictionary:self.dict1]; self.dict3 = [self.dict1 copy]; self.dict4 = [NSMutableDictionary dictionaryWithDictionary:self.dict1]; self.errorTypeDict = @{@1:@1,@2:@2,@3:@3}; self.boolValue = YES; self.boolPtrValue = &_boolValue; } return self; } -(void)printBool:(BOOL)boolValue { NSLog(@"boolValue = %d",boolValue); } -(void)printBoolPtr:(BOOL*)boolPtrValue { if( boolPtrValue==nil ){ NSLog(@"boolPtrValue = nil"); return ; } NSLog(@"boolValue = %d",*boolPtrValue); }
main. The code for calling class Swift in M is as follows:
#import "studyObjc-Swift.h" int main(int argc, const char * argv[]) { @autoreleasepool { [[[TestSwift alloc] init] main]; } return 0; }
Swift's code framework is as follows:
import Foundation @objcMembers class TestSwift:NSObject { private func testDict1(){ } private func testDict2(){ } private func testDict3(){ } private func testDict4(){ } private func testErrorTypeDict(){ } private func testBool(){ } private func testBoolPtr(){ } public func main(){ testDict1() testDict2() testDict3() testDict4() testErrorTypeDict() testBool() testBoolPtr() } }
3, Usage in Swift under different data types
Next, we start trying to call OC's TestDataModel in Swift code.
1. Use NSDictionary in Swift
First of all, if we use the testDict1 method, we can directly write it as follows and compile it.
private func testDict1(){ let swDict1:[String:Any] = TestDataModel.init().dict1 print(swDict1) }
At this time, the compiler will report an error:
Cannot assign value of type '[AnyHashable : Any]' to type '[String : Any]'
The reason is that when NSDictionary does not use generics to specify the type, by default, its type will be considered as [anyhashable: any] in Swift
At this time, in order to successfully assign the value of dict1 to swDict1, we must perform a type conversion. The code is as follows:
private func testDict1(){ let swDict1:[String:Any] = TestDataModel.init().dict1 as! [String:Any] print(swDict1) }
It Works, it looks good.
But in fact, when you see as! You should realize that when the cast type of dict1 fails to convert to [String:Any], the program will crash!!!
2. Type conversion failure leads to program crash
To prove this, next, we implement the following writing method in the testErrorTypeDict method:
private func testErrorTypeDict(){ let swErrorTypeDict:[String:Any] = TestDataModel.init().errorTypeDict as! [String:Any] print(swErrorTypeDict) }
Compile, run and crash the program, and print the following information:
Could not cast value of type 'Swift.AnyHashable' (0x7fff81603d58) to 'Swift.String' (0x7fff81606180).
3. Use NSMutableDictionary in Swift
Next, we try to use the attribute of NSMutableDictionary type in Swift. When using it, we also need to cast it. The code is as follows:
private func testDict2(){ let swDict2:[String:Any] = TestDataModel.init().dict2 as! [String:Any] print(swDict2) }
4. Use NSDictionary + paradigm in Swift
Finally, let's try to use the attribute of nsdictionary < nsstring *, ID > type in Swift. The code is as follows:
private func testDict3(){ let swDict3:[String:Any] = TestDataModel.init().dict3 print(swDict3) }
We can see that the assignment process is extremely smooth and smooth, and there is no need for any type conversion at the code level.
5. Use NSMutableDictionary + paradigm in Swift
So, here's the question. Can nsmutabledictionary < nsstring *, ID > be assigned smoothly and smoothly?
The answer is no, and the code is as follows:
private func testDict4(){ let swDict4:[String:Any] = TestDataModel.init().dict4 as! [String : Any] print(swDict4) }
6. Use Bool and Bool in Swift*
I'm tired of writing and omit nonsense. The counterexample of the last suggestion is as follows:
private func testBool(){ let swBool:Bool = true TestDataModel.init().print(swBool) } private func testBoolPtr(){ let swBool:ObjCBool = true let swBoolPtr = UnsafeMutablePointer<ObjCBool>.init(&swBool) TestDataModel.init().printBoolPtr(swBoolPtr) }
We found that once the basic data type in OC uses a pointer, it is necessary to manually create an unsafe mutablepointer object in Swift, which is very troublesome to use. Therefore, try to avoid using a pointer / secondary pointer in the parameters or attributes of OC methods.
From the example in this article, we can deeply understand the benefits of the suggestions mentioned above from the level of Swift use. Then, how to realize the transformation of OC object to Swift object? Please refer to below.
4, Appendix: Swift complete code
Swift's complete code
import Foundation @objcMembers class TestSwift:NSObject { private func testDict1(){ let swDict1:[String:Any] = TestDataModel.init().dict1 as! [String:Any] print(swDict1) } private func testDict2(){ let swDict2:[String:Any] = TestDataModel.init().dict2 as! [String:Any] print(swDict2) } private func testDict3(){ let swDict3:[String:Any] = TestDataModel.init().dict3 print(swDict3) } private func testDict4(){ let swDict4:[String:Any] = TestDataModel.init().dict4 as! [String : Any] print(swDict4) } private func testErrorTypeDict(){ // let swErrorTypeDict:[String:Any] = TestDataModel.init().errorTypeDict as! [String:Any] // print(swErrorTypeDict) } private func testBool(){ let swBool:Bool = true TestDataModel.init().print(swBool) } private func testBoolPtr(){ var swBool:ObjCBool = true var swBoolPtr = UnsafeMutablePointer<ObjCBool>.init(&swBool) TestDataModel.init().printBoolPtr(swBoolPtr) } public func main(){ testDict1() testDict2() testDict3() testErrorTypeDict() testBool() testBoolPtr() } }