Running environment: Xcode 11.1 swift5.0
Recently, I participated in a project that needed to be transferred from Objective-C (hereinafter referred to as OC) to swift. During this period, some pits were encountered, so this summary document was provided.
If you also have the need to make OC project swift, it can be used as a reference.
There is a big premise for OC to swift is that you should have a certain understanding of swift and be familiar with swift syntax. You’d better read the official one completelyLanguage Guide。
Since the recognition rate of automation tools is not satisfactory, manual conversion is needed in most cases.
Automation tools
There is a better automation toolSwiftifyIt can convert OC files and even OC projects into swift, and the accuracy rate is claimed to be up to 90%. I tried some of the features in the free version, but I felt that the effect was not ideal, because I had not used the paid version, so it was not good to evaluate it.
Swiftify also has an Xcode plug-inSwiftify for XcodeCan realize the conversion of selected code and single file. This plug-in is quite good, and it is accurate for pure system code conversion, but some code still has some identification problems, which need to be modified manually.
Manual swift
Bridging documents
If you are using swift code for the first time in a project, Xcode will prompt you to add one when you add a swift file.h
Bridge file for. If you don’t add it carefully, you can import it manually, that is, you can manually generate one.h
File, and then in theBuild Settings > Swift Compiler - General > Objective-C Bridging Header
Fill in the.h
The path to the file.
The function of this bridging file is for swift code to reference OC code or OC’s three-party library.
#import "Utility.h"
#import
Copy code
stayBridging Header
There is also a configuration item belowObjective-C Generated Interface Header Name
The corresponding value isProjectName-Swift.h
。 This is a hidden header file automatically generated by Xcode. In each build process, the part declared as external call in swift code will be converted into OC code, and the file in OC part will be similarpch
The header file is referenced globally. Because it is generated in the build process, only.m
It can be directly referenced in the.h
References in the document are described below.
Appdelegate (program entry)
Not in swiftmain.m
Documents, replaced by@UIApplicationMain
Command, which is equivalent to the original executionmain.m
。 So we can putmain.m
Remove the file.
System API
aboutUIKit
Most of the code conversion in the framework can be converted directly by looking at the system API documents, which will not be discussed here.
Property (property)
Swift didn’tproperty
Neithercopy
,nonatomic
Attribute modifier, which only indicates whether the property is variable or notlet
andvar
。
Attention point oneA class in OC.h
and.m
Two files represent the method, variable, and method variable for internal use only. When migrating to swift, you should.m
Property in is marked asprivate
In other words, the external connection cannot be directly accessed.h
The property in the is not handled, and the default property is selectedinternal
, that is, the same module can be accessed.
The same is true for function migration.
Note twoA special case is that in OC projects, some properties are internal(.m
)Variable, external(.h
)Read only. This situation can be dealt with as follows:
private(set) var value: String
Copy code
It’s just rightvalue
Ofset
The method will doprivate
Mark.
Note 3There is a special symbol for null types in swift?
, corresponding to thenil
。 There is no such symbol in OC, but you can use thenullable
andnonnull
Indicates whether the property, method parameter or return value can be null.
If OC does not declare whether an attribute can be empty, it will go to the default valuenonnull
。
If we want all the properties of a class, the return value of the function isnonnull
In addition to manually adding them one by one, there is a macro command.
NS_ASSUME_NONNULL_BEGIN
/* code */
NS_ASSUME_NONNULL_END
Copy code
This is my IOS development exchange group:519832104No matter you are Xiaobai or Daniel, welcome to settle in. You can share experience, discuss technology, learn and grow together!
Also attached is a large factory interview questions collected by friends. You need IOS development learning materials and interview real questions, which can be obtained by entering the group!
Click here to communicate with IOS Daniel immediately
Enum (enumeration)
OC Code:
typedef NS_ENUM(NSInteger, PlayerState) {
PlayerStateNone = 0,
PlayerStatePlaying,
PlayerStatePause,
PlayerStateBuffer,
PlayerStateFailed,
};
typedef NS_OPTIONS(NSUInteger, XXViewAnimationOptions) {
XXViewAnimationOptionNone = 1 << 0,
XXViewAnimationOptionSelcted1 = 1 << 1,
XXViewAnimationOptionSelcted2 = 1 << 2,
}
Copy code
Swift Code:
enum PlayerState: Int {
case none = 0
case playing
case pause
case buffer
case failed
}
struct ViewAnimationOptions: OptionSet {
let rawValue: UInt
static let None = ViewAnimationOptions(rawValue: 1<<0)
static let Selected1 = ViewAnimationOptions(rawValue: 1<<0)
static let Selected2 = ViewAnimationOptions(rawValue: 1 << 2)
//...
}
Copy code
Swift didn’tNS_OPTIONS
Instead of the concept of satisfactionOptionSet
Agreedstruct
Type.
Lazy loading
OC Code:
- (MTObject *)object {
if (!_object) {
_object = [MTObject new];
}
return _object;
}
Copy code
Swift Code:
lazy var object: MTObject = {
let object = MTObject()
return imagobjecteView
}()
Copy code
closure
OC Code:
typedef void (^DownloadStateBlock)(BOOL isComplete);
Copy code
Swift Code:
typealias DownloadStateBlock = ((_ isComplete: Bool) -> Void)
Copy code
Single case
OC Code:
+ (XXManager *)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
Copy code
Swift implements singleton in two ways
The first one
Let shared = xxmanager() // declare in global namespace
Class XXManager {
}
Copy code
You may wonder, why notdispatch_once
How to ensure the uniqueness of multi thread creation? In fact, the global variable in swift is lazy loaded and initialized in appdelegate. After that, all calls will use this instance. And the initialization of global variables is used by defaultdispatch_once
This ensures that the initializer of the global variable will only be called onceshard
OfAtomicity。
The second kind
Class XXManager {
static let shared = XXManager()
private override init() {
// do something
}
}
Copy code
Swift 2 is starting to increasestatic
Keyword, which is used to define the scope of a variable. If not usedstatic
So every one of themshared
Will correspond to an instance. And usestatic
After that,shared
To be a global variable is consistent with the principle of the first method above. Notice that the constructor uses theprivate
Keyword, so it also guarantees the atomicity of the singleton.
Initialization methods and destructors
For the initialization method, OC calls the initialization method of the parent class first, and then initializes its own member variables. Swift first initializes its own member variables, and then calls the initialization method of the parent class.
OC Code:
//Initialization method
@interface MainView : UIView
@property (nonatomic, strong) NSString *title;
- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title NS_DESIGNATED_INITIALIZER;
@end
@implementation MainView
- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title {
if (self = [super initWithFrame:frame]) {
self.title = title;
}
return self;
}
@end
//Destructor
- (void)dealloc {
//dealloc
}
Copy code
When the above class is called
Swift Code:
class MainViewSwift: UIView {
let title: String
init(frame: CGRect, title: String) {
self.title = title
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
//deinit
}
}
Copy code
function call
OC Code:
//Instance function (common method)
- (void)configModelWith:(XXModel *)model {}
//Instance function (private method)
- (void)calculateProgress {}
//Class function
+ (void)configModelWith:(XXModel *)model {}
Copy code
//Instance function (common method)
func configModel(with model: XXModel) {}
//Instance function (private method)
private func calculateProgress() {}
//Class function (cannot be overridden by subclasses)
static func configModel(with model: XXModel) {}
//Class function (can be overridden by subclasses)
class func configModel(with model: XXModel) {}
//Class function (cannot be overridden by subclasses)
class final func configModel(with model: XXModel) {}
Copy code
OC can declare the method in the.h
The file indicates whether the method is private. Not in swift.h
File, the permission control of methods is carried out through permission keywords, and the permission size of each keyword is as follows:private < fileprivate < internal < public < open
amonginternal
Is the default permission, can be in the samemodule
Next visit.
Nsnotification
OC Code:
// add observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(method) name:@"NotificationName" object:nil];
// post
[NSNotificationCenter.defaultCenter postNotificationName:@"NotificationName" object:nil];
Copy code
Swift Code:
// add observer
NotificationCenter.default.addObserver(self, selector: #selector(method), name: NSNotification.Name(rawValue: "NotificationName"), object: nil)
// post
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NotificationName"), object: self)
Copy code
Notice that the notification center in swiftNotificationCenter
NoNS
Prefix, notification name changed from string toNSNotification.Name
The structure of.
The purpose of changing to structure is to facilitate the management of strings. The original string type has been changed to specifiedNSNotification.Name
Type. The above swift code can be modified as follows:
extension NSNotification.Name {
static let NotificationName = NSNotification.Name("NotificationName")
}
// add observer
NotificationCenter.default.addObserver(self, selector: #selector(method), name: .NotificationName, object: nil)
// post
NotificationCenter.default.post(name: .NotificationName, object: self)
Copy code
Protocol
OC Code:
@protocol XXManagerDelegate
- (void)downloadFileFailed:(NSError *)error;
@optional
- (void)downloadFileComplete;
@end
@interface XXManager: NSObject
@property (nonatomic, weak) id delegate;
@end
Copy code
Swiftprotocol
It’s not just thatclass
Object,struct
andenum
Can also implement the protocol. It should be noted thatstruct
andenum
Is a reference type and cannot be usedweak
modification. Can be used only if the current proxy supports only class objectsweak
。 To convert the above code into the corresponding swift code, that is:
@objc protocol XXManagerDelegate {
func downloadFailFailed(error: Error)
@Objc optional func downloadfilecomplete() // implementation of Optional Protocol
}
class XXManager: NSObject {
weak var delegate: XXManagerDelegate?
}
Copy code
@objc
Is to indicate that the current code is forNSObject
Object, that isclass
You can use the weak object.
If it is not a delegate for the nsobject object, but just a normal class object, you can set the proxy as follows:
protocol XXManagerDelegate: class {
func downloadFailFailed(error: Error)
}
class XXManager {
weak var delegate: XXManagerDelegate?
}
Copy code
It is worth noting that only@objc
Markedprotocol
have access to@optional
。
Notes of swift and OC Hybrid Editing
Change of function name
If you define a delegate method in a swift class:
@objc protocol MarkButtonDelegate {
func clickBtn(title: String)
}
Copy code
If you want to implement this protocol in OC, the method name will become:
- (void)clickBtnWithTitle:(NSString *)title {
// code
}
Copy code
This is mainly because Swift has a specified parameter tag, but OC does not. When the OC method name is generated from the swift method name, the compiler will automatically add some modifiers to make the function “smooth” as a sentence.
Call the swift class in the OC header file
If you want to reference the swift class in the OC header file, because swift does not have a header file, in order to make the swift class recognized in the header file, you need to use the@class
The method is introduced.
@class SwiftClass;
@interface XXOCClass: NSObject
@property (nonatomic, strong) SwiftClass *object;
@end
Copy code
Rename OC class under swift call
Because Swift has a namespace for different modules, the swift class does not need to be prefixed. If there is an OC public component with prefix, it is not elegant to have to specify prefix when calling in swift environment, so Apple added a macro commandNS_SWIFT_NAME
To allow the OC class to be renamed in swift environment:
NS_SWIFT_NAME(LoginManager)
@interface XXLoginManager: NSObject
@end
Copy code
So we willXXLoginManager
In swift environment, the class name is changed toLoginManager
。
Reference types and value types
struct
Andenum
Is value type, classclass
Is a reference type.String
,Array
AndDictionary
They are structures, so the assignment is directly a copy, andNSString
,NSArray
andNSDictionary
It’s a class, so it’s a way to use references.struct
Compareclass
More lightweight,struct
In the stack,class
Allocated in the heap.
ID type and anyobject
OCid
Type is automatically converted toAnyObject
They are very similar, but they are not identical in concept. Another concept in swift isAny
The differences among them are as follows:
id
Is a general object type, which can point to objects belonging to any class. In OC, it can represent all objects inherited fromNSObject
Object.AnyObject
Can represent anythingclass
An instance of type.Any
Can represent any type, even includefunc
Type.
From the range size comparison, it is:id < AnyObject < Any
。
Other grammatical differences and precautions (to be added)
1. Semicolons are not required in swift statements;
。
2. The bool type is more strict. Swift is no longer a non-0 in OC, which is true. True and false only correspond to each othertrue
andfalse
。
3. In general, there is no need to write in swift classself
But it needs to be written in the closure.
4. Swift is a strongly typed language, and you must specify an explicit type. In swiftInt
andFloat
They can’t be operated directly. They must be converted to the same type.
5. Swift abandoned the traditional++
,--
Operation, abandon the traditional C language typefor
Circulation, instead offor-in
。
6. Swift’sswitch
Operations, which do not need to be added at the end of each case statementbreak
。
7. Swift rightenum
It can support any type, while OC enumeration only supportsInt
Type. If you want to write compatible code, select int type enumeration.
8. To be called by OC, swift code needs to be preceded by property and method names@objc
。
9. Swift’s unique features, such as generics,struct
, non int typeenum
And so on are included in the function parameter, even if added@objc
It will not be passed by the compiler.
10. Swift supports overloading, OC does not.
11. The swift function with default value will be expanded automatically when it is called by OC again.
Grammar check
There are many details about the syntax changes after OC to swift. Especially for those who use swift for the first time, it is easy to miss or wait for OC’s idea to write code. A framework for syntax checking is recommended hereSwiftLintCan automatically check whether our code conforms to the swift specification.
It can be done throughcocoapods
After configuration, each timeBuild
The lint script will perform the syntax check operation of swift code once. Lint will also grade the code specifications. Serious code errors will directly report errors, which will cause the program to fail to start. If not, code warnings will be displayed( A kind of ️)。
If you feel that swiftlint is a little too strict, you can also modify it.swiftlint.yml
File, custom belongs to their own syntax specification.