Swift Runtime?

Source: Internet
Author: User
You must have thought about it

 

      I believe that every iOS developer knows Runtime, and now Swift has been updated to version 4.0. If you have also learned Swift, you may have thought about such a problem. Everyone in OC is dynamic. You Can get the property method you want through the runtime API, etc. What about Swift? Is it the same as OC?

      I thought about this problem when I was watching Swift. With this question, I concluded this article today.

      Let's talk about this Runtime. In my previous article, I have summarized the runtime of OC, some basic methods of its API, and some specific uses in the project. Here I will briefly mention the basic concepts of Runtime:

      RunTime for short. OC is the runtime mechanism, that is, some mechanisms at runtime, the most important of which is the message mechanism. For the C language we are familiar with, the function call determines which function to call when compiling. However, the OC function is a dynamic calling process. At compile time, it is not possible to decide which function is actually called. Only when it is actually running, it will find the corresponding function to call according to the function name.

     There are two conclusions:

     1. During the compilation phase, OC can call any function, even if this function is not implemented, as long as it is declared, no error will be reported.

     2. During the compile phase, C calls an unimplemented function and it will report an error.

 

Take a look at Swift Runtime

   

      Without first throwing a conclusion, start with the following simple code and find out the answer we want step by step:

      We define a pure Swift class TestASwiftClass with the following code:

class TestASwiftClass {
        
        var aBoll: Bool = true
        var aInt: Int = 0
        func testReturnVoidWithaId (aId: UIView) {
                
            print ("TestASwiftClass.testReturnVoidWithaId")
        }
}
       The code is also very simple. We have defined two variables and one method. Let's write the code in ViewController that inherits from UIViewController:

class ViewController: UIViewController {

    let testStringOne = "testStringOne"
    let testStringTwo = "testStringTwo"
    let testStringThr = "testStringThr"
    var count: UInt32 = 0

    override func viewDidLoad () {
        super.viewDidLoad ()
        // Do any additional setup after loading the view.

        let SwiftClass = TestASwiftClass ()
        let proList = class_copyPropertyList (object_getClass (SwiftClass), & count)
        for i in 0 .. <numericCast (count) {
                
                let property = property_getName (proList? [i]);
                print ("Property member property:% @", String.init (utf8String: property!) ?? "No property you want" was found);
        }
    }
    override func didReceiveMemoryWarning () {
        super.didReceiveMemoryWarning ()
        // Dispose of any resources that can be recreated.
    }
}
      The above code is also very simple. We add some variables to the ViewController, and then try to get the properties of the pure Swift class TestASwiftClass we defined above by using the Runtime method. If you run the above code, you will find:

      Nothing at all! !! !! why? ?

      Below we give the answer, use it to explain why we did not get anything through the Runtime API above, and then use OC to prove some of our conclusions:

      The C language decides which function to call when the function is compiled. During the compilation phase, C will report an error if it calls a function that is not implemented.

      OC functions are called dynamically. At compile time, you ca n’t decide which function to call. You can only decide which function to call at runtime. During compilation, OC can call any function, even if the function is Implementation, as long as it is declared, no error will be reported.

      Swift's pure Swift class function call is no longer sent by the OC runtime. Similar to C, which function is called at the compilation stage, so we can't get its properties and Method

      Swift class inherited from OC, in order to be compatible with OC, everything inherited and OC retains its characteristics, so you can use Runtime to get its properties and methods

      In view of the conclusions given above, are we looking at whether Swift retains all the features of OC for classes that inherit from OC? Look at the following code again, just make a simple modification and write the object obtained by the object_getClass method as self:

let proList = class_copyPropertyList (object_getClass (self), & count)
for i in 0 .. <numericCast (count) {
                
       let property = property_getName (proList? [i]);
       print ("Property member property:% @", String.init (utf8String: property!) ?? "No property you want" was found);
}
The logs we obtained through the above method are as follows:

     

 

You can see that we get the variables we defined in the ViewController. This also proves that it is indeed what the answer above said.

 

Then this brings up another problem

   

      So Swiftw can't use Runtime?

      Think about it, if really Swift can't take advantage of Runtime, that would be a much more disappointing thing! The answer is definitely no, we can still use Swift with Runtime. Look at the following code:

class TestASwiftClass {
        
        dynamic var aBoll: Bool = true
        var aInt: Int = 0
        dynamic func testReturnVoidWithaId (aId: UIView) {
                
            print ("TestASwiftClass.testReturnVoidWithaId")
        }
}
      The above is the TestASwiftClass class we defined. I don't know if you notice it in different places?

      Well, we used the dynamic keyword. We added this keyword before the definition of the first variable and method. What changed after we added this keyword? Let's try it through the code that we obtained the pure Swift class from the beginning, and see the results!

 

       result:

              It can be seen that the variables are obtained here (there is no method to obtain the property, so the method is not available),

              The aBoll variable is preceded by the dynamic keyword, which we have obtained. We did not add the aInt variable before, so we can see that we did not get this variable. The key is to understand the meaning of the dynamic keyword:

      @objc is used to export the Swift API for use by Object-C and Runtime. If your class inherits from the OC class, this identifier will be automatically added. The properties and methods of this identifier cannot be guaranteed. It will be called at runtime, because Swift will perform static optimization. To fully declare it as a dynamic call, it must be decorated with the dynamic identifier. Of course, when dynamic is added, he will add the @objc identifier himself.

   

Runtime

 

      After explaining this keyword, I just got a basic understanding of Swift's Runtime. The following code is just like OC:

      1. Obtaining method:

let mthList = class_copyMethodList (object_getClass (SwiftClass), & count)
for index in 0 .. <numericCast (count) {
                
     let method = method_getName (mthList? [index])
     print ("Attribute member method:% @", String.init (NSStringFromSelector (method!)) ?? "No method you found")
}
      2.Attribute member variables

 let IvarList = class_copyIvarList (object_getClass (SwiftClass), & count)
 for index in 0 .. <numericCast (count) {
                
     let Ivar = ivar_getName (IvarList? [index])
     print ("Attribute member variable:% @", String.init (utf8String: Ivar!) ?? "No member variable you found"
}
      3.Protocol list

let protocalList = class_copyProtocolList (object_getClass (self), & count)
for index in 0 .. <numericCast (count) {
                
     let protocal = protocol_getName (protocalList? [index])
     print ("protocol:% @", String.init (utf8String: protocal!) ?? "No protocol you found"
}
      4.Method exchange

           This is one of the key points of Runtime, let's talk about it carefully.

           The most commonly used OC dynamic is actually method replacement, which replaces the method of a certain class with its own defined class, so as to achieve the role of Hook.

           For pure Swift classes, you can't replace the methods because you know that you ca n’t get the class properties and methods of the previous tests. However, for classes that inherit from NSObject, because they integrate all the features of OC, it is possible Use Runtime properties for method substitution, remember the dynamic keyword we said earlier.

func ChangeMethod ()-> Void {
        
        // Get the method before the exchange
        let originaMethodC = class_getInstanceMethod (object_getClass (self), #selec
tor (self.originaMethod))
        // Get the method after the exchange
        let swizzeMethodC = class_getInstanceMethod (object_getClass (self), #selector (self.swizzeMethod))

         // Replace the implementation of the existing method in the class, add the method if the method does not exist
         // Get method's Type string (including parameter type and return value type)
         // class_replaceMethod (object_getClass (self), #selector (self.swizzeMethod), method_getImplementation (originaMethodC), method_getTypeEncoding (originaMethodC))
        
         print ("You swap the implementation of the two methods")
         method_exchangeImplementations (originaMethodC, swizzeMethodC)
    }
 
    dynamic func originaMethod ()-> Void {
                
         print ("I am the method before swap")
    }
       
    dynamic func swizzeMethod ()-> Void {
                
         print ("I am the method after the swap")
    }
      5.Associated attributes

           If the above method Hook is more important, this association property is also more important. When I summarized the OC Runtime earlier, I added a demo here in the method addition. Let ’s reorganize this Demo. The navigation gradient is Use Runtime to add attributes to the navigation to achieve.

extension UINavigationBar {
        
    var navigationGradualChangeBackgroundView: UIView? {
    
        get {
            return objc_getAssociatedObject (self, & self.navigationGradualChangeBackgroundView) as? UIView;
        }
        set {
        
            objc_setAssociatedObject (self, & self.navigationGradualChangeBackgroundView, navigationGradualChangeBackgroundView, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    
    func setNavigationBackgroundColor (backgroundColor: UIColor)-> Void {
        
        if (self.navigationGradualChangeBackgroundView == nil) {
            
            self.setBackgroundImage (UIImage (), for: UIBarMetrics.default)
            self.navigationGradualChangeBackgroundView = UIView.init (frame: CGRect.init (x: 0, y: -20, width: SCREENWIDTH, height: self.bounds.size.height + 20))
            self.navigationGradualChangeBackgroundView! .isUserInteractionEnabled = false
            self.insertSubview (self.navigationGradualChangeBackgroundView !, at: 0)
        }
    
       self.navigationGradualChangeBackgroundView! .backgroundColor = backgroundColor
    }
    
    func removeNavigationBackgroundColor ()-> Void {
        
       self.setBackgroundImage (nil, for: UIBarMetrics.default)
       self.navigationGradualChangeBackgroundView! .removeFromSuperview ()
       self.navigationGradualChangeBackgroundView = nil
    }
}
 1. The above is written by adding extensions to UINavigationBar. Note that the writing of Swift is different from that of OC.

 2. When applying, directly generate the Color by changing the scroll distance when scrolling, and call the setNavigationBackgroundColor method in the scrolling method.

 

See another example

       

      When sorting out the information, I found an article: iOS --- Three ways to prevent repeated UIButton clicks

      In the final solution using the Runtime method, the last piece of code is:

 

 

Explanation:

      You can see that in the end, I directly exchanged the method I defined and the method of the system, and the focus is on the implementation in my method! You can see that the time judgment is added in front of the method, and finally the method itself is called! There is a problem. You used your own method instead of the system method, and added some of your own things. Have you ever called the system method again? You don't know the specific content of the system method but directly replaced it with your own method, then the function of the system button must be affected! You should understand what I mean.

      Remember: The purpose of using Method Swizzling is usually to add functionality to the program, not to completely replace a certain function, so we generally need to call the original implementation in a custom implementation.

      In particular, I would like to explain this point in detail. How to amend it? In fact, the students below the article also gave answers. The specific content is recommended to everyone to look at this article, and you should get something!

      Objective-C Method Swizzling Best Practices

      

Swift Runtime?
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.