Suggestions on data types in the [OC/Swift hybrid] interface: use level analysis

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()
    }
    
}

Keywords: Swift iOS objective-c

Added by Dan400007 on Sat, 29 Jan 2022 12:40:12 +0200