Experts say: This is an iOS development skill that you can’t miss (Part 2)

Experts say: This is an iOS development skill that you can’t miss (Part 2)

I changed to a new factory, and it's been less than a month. Oh, it's really tiring, basically the rhythm of 996.5, only more. I'm almost sick of overtime, but I can't help myself. I don't care about my life just to make a living. Who made me just a lousy code writer? But I have to work overtime a lot, but I'm too ugly, so there's nothing I can do. I have to make time to study even if I don't have time. Otherwise, I won't have food to eat, and I have to support my family.

There is not much content in this summary, mainly the following issues:

  • Use UIVisualEffectView to add special effects to views
  • Nullability Annotations
  • The life cycle of weak

Use UIVisualEffectView to add special effects to views

After iOS 8, Apple opened up a number of interfaces for creating special effects, including an interface for creating frosted glass (blur).

Usually, if you want to create a special effect (such as blur effect), you can create a UIVisualEffectView view object, which provides a simple way to achieve complex visual effects. This object can be regarded as a container for the effect. The actual effect will affect the content under the view object, or the content added to the contentView of the view object.

Let's take an example to see how to use UIVisualEffectView:

  1. let bgView: UIImageView = UIImageView(image: UIImage(named: "visual" ))
  2. bgView.frame = self.view.bounds
  3. self.view.addSubview(bgView)
  4.  
  5. let blurEffect: UIBlurEffect = UIBlurEffect(style: .Light)
  6. let blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect)
  7. blurView.frame = CGRectMake( 50.0 , 50.0 , self.view.frame.width - 100.0 , 200.0 )
  8. self.view.addSubview(blurView)

This code adds a UIImageView as the background image to the current view controller.

We can see that UIVisualEffectView is very simple. It should be noted that subviews should not be added directly to the UIVisualEffectView view, but should be added to the contentView of the UIVisualEffectView object.

In addition, try to avoid setting the alpha value of the UIVisualEffectView object to a value less than 1.0, because creating a semi-transparent view will cause the system to perform blending operations on the UIVisualEffectView object and all related subviews during off-screen rendering. This not only consumes CPU/GPU, but may also cause many effects to display incorrectly or not display at all.

We saw above that the method to initialize a UIVisualEffectView object is UIVisualEffectView(effect: blurEffect), which is defined as follows:

  1. init(effect effect: UIVisualEffect)

The parameter of this method is a UIVisualEffect object. Looking at the official documentation, we can see that in UIKit, several objects are defined specifically for creating visual effects, namely UIVisualEffect, UIBlurEffect, and UIVibrancyEffect. Their inheritance hierarchy is as follows:

  1. NSObject
  2. |--UIVisualEffect
  3. |--UIBlurEffect
  4. |--UIVibrancyEffect

UIVisualEffect is a base class for creating visual effects that inherits from NSObject. However, this class does not provide any new properties and methods other than those inherited from NSObject. Its main purpose is to initialize UIVisualEffectView, in which you can pass in UIBlurEffect or UIVibrancyEffect objects.

A UIBlurEffect object is used to apply a blur effect to the content below the UIVisualEffectView view, as shown in the example above. However, the effect of this object does not affect the content in the contentView of the UIVisualEffectView object.

UIBlurEffect mainly defines three effects, which are determined by the enumeration UIBlurEffectStyle, which is defined as follows:

  1. enum UIBlurEffectStyle : Int {
  2. case ExtraLight
  3. case Light
  4. case Dark
  5. }

It mainly determines the blending of the special effect view and the bottom view based on the hue.

Unlike UIBlurEffect, UIVibrancyEffect is mainly used to amplify and adjust the color of the content under the UIVisualEffectView view, while making the content in the contentView of UIVisualEffectView look more vivid. Usually the UIVibrancyEffect object is used together with UIBlurEffect, mainly used to process some display effects on the UIBlurEffect special effect. Following the above code, let's see how to add some new special effects to the blurred view, as shown in the following code:

  1. l et vibrancyView: UIVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(forBlurEffect: blurEffect))
  2. vibrancyView.setTranslatesAutoresizingMaskIntoConstraints( false )
  3. blurView.contentView.addSubview(vibrancyView)
  4.  
  5. var label: UILabel = UILabel()
  6. label.setTranslatesAutoresizingMaskIntoConstraints( false )
  7. label.text = "Vibrancy Effect"  
  8. label.font = UIFont(name: "HelveticaNeue-Bold" , size: 30 )
  9. label.textAlignment = .Center
  10. label.textColor = UIColor.whiteColor()
  11. vibrancyView.contentView.addSubview(label)

     

The vibrancy effect depends on the color value. All subviews added to contentView must implement the tintColorDidChange method and update themselves. It should be noted that when we use the UIVibrancyEffect(forBlurEffect:) method to create a UIVibrancyEffect, the parameter blurEffect must be the blurEffect we want to add the effect to, otherwise it may not be the effect we want.

In addition, UIVibrancyEffect also provides a class method notificationCenterVibrancyEffect, which is declared as follows:

class func notificationCenterVibrancyEffect() -> UIVibrancyEffect!

This method creates a vibrancy effect for the Today extension in the Notification Center.

refer to

UIVisualEffectView Class Reference

UIVisualEffect Class Reference

 

UIBlurEffect Class Reference

 

UIVibrancyEffect Class Reference UIVisualEffect – Swift Tutorial iOS 8: UIVisualEffect

Pointer is missing a nullability type specifier (nonnull or nullable) problem handling— Nullability Annotations

I'm writing code with Xcode 6.3 recently, and some code involving objects will report the following compiler warning:

  1. Pointer is missing a nullability type specifier (__nonnull or __nullable)

So I googled it and found that this is a new feature of Xcode 6.3, namely nullability annotations.

#p#

Nullability Annotations

We all know that in Swift, we can use ! and ? to indicate whether an object is optional or non-optional, such as view? and view!. In Objective-C, there is no such distinction. View can indicate that the object is optional or non-optional. This will cause a problem: when Swift and Objective-C are mixed, the Swift compiler does not know whether an Objective-C object is optional or non-optional, so in this case the compiler will implicitly treat the Objective-C object as non-optional.

To solve this problem, Apple introduced a new feature of Objective-C in Xcode 6.3: nullability annotations. The core of this new feature is two new type annotations: __nullable and __nonnull. As we can guess from the literal meaning, __nullable means that the object can be NULL or nil, while __nonnull means that the object should not be empty. When we do not follow this rule, the compiler will give a warning.

Let's look at the following example,

  1. @interface TestNullabilityClass ()
  2.  
  3. @property (nonatomic, copy) NSArray * items;
  4.  
  5. - (id)itemWithName:(NSString * __nonnull)name;
  6.  
  7. @end  
  8.  
  9. @implementation TestNullabilityClass
  10.  
  11. ...
  12.  
  13. - ( void )testNullability {
  14.  
  15. [self itemWithName:nil]; // Compiler warning: Null passed to a callee that requires a non-null argument  
  16. }
  17.  
  18. - (id)itemWithName:(NSString * __nonnull)name {
  19. return nil;
  20. }
  21.  
  22. @end  

However, this is just a warning and the program can still be compiled and run.

In fact, __nullable and __nonnull can be used anywhere the const keyword can be used, but these two keywords are limited to pointer types. In the method declaration, we can also use nullable and nonnull without underscores, as shown below:

  1. - (nullable id)itemWithName:(NSString * nonnull)name

In the property declaration, two corresponding attributes are also added, so the items property in the above example can be declared as follows:

  1. @property (nonatomic, copy, nonnull) NSArray * items;

Of course, you can also use the following method:

  1. @property (nonatomic, copy) NSArray * __nonnull items;

It is recommended to use nonnull, which makes the property declaration clearer.

Nonnull Region Settings (Audited Regions)

It would be very tedious to specify nonnull and nullable for each attribute or method. In order to reduce our workload, Apple provides two macros: NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END. In the code between these two macros, all simple pointer objects are assumed to be nonnull, so we only need to specify those nullable pointers. As shown in the following code:

  1. NS_ASSUME_NONNULL_BEGIN
  2. @interface TestNullabilityClass ()
  3.  
  4. @property (nonatomic, copy) NSArray * items;
  5.  
  6.  
  7. - (id)itemWithName:(nullable NSString *)name;
  8.  
  9. @end  
  10. NS_ASSUME_NONNULL_END

In the above code, the items property is nonnull by default, the return value of the itemWithName: method is also nonnull, and the parameter is specified as nullable.

However, for safety reasons, Apple has also established several rules:

  • The nullability of a type defined by a typedef is often context-dependent and cannot be assumed to be nonnull, even in Audited Regions.
  • Complex pointer types (such as id *) must explicitly specify whether they are nonnull or nullable. For example, to specify a nonnull pointer to a nullable object, you can use "__nullable id * __nonnull".
  • The NSError** that we often use is usually assumed to be a nullable pointer to a nullable NSError object.

compatibility

Because Nullability Annotations are newly added in Xcode 6.3, we need to consider the old code. In fact, Apple has already helped us deal with this compatibility issue, and we can use them safely:

Old code will still work fine, even if nil is used for nonnull objects.

When old code needs to be mixed with Swift, a warning will be given under the new Swift compiler.

nonnull does not affect performance. In fact, we can still check whether our object is nil at runtime.

In fact, we can think of nonnull/nullable together with our assertions and exceptions, and they all deal with the same problem: violating the contract is a programmer's mistake. In particular, the return value is something we can control, and if the return value is nonnull, we should not return nil unless it is for backward compatibility.

refer to

Nullability and Objective-C

#p#

The life cycle of weak

We all know that weak refers to a weak reference, which does not increase the reference count of the object, and after the object it points to is released, the weak pointer will be set to nil. Weak references are usually used to deal with circular references, such as in the use of proxies and blocks, which are relatively more commonly used.

I have a little understanding of the implementation of weak before, and I know its basic life cycle, but I don't know how it is implemented clearly. Today I read the explanation of __weak in "Advanced Programming in Objective-C" again, and I'll take a note here.

Let's take the following line of code as an example:

Code Listing 1: Sample code

  1. {
  2. id __weak obj1 = obj;
  3. }

When we initialize a weak variable, the runtime calls the objc_initWeak function. The declaration of this function in Clang is as follows:

  1. id objc_initWeak(id *object, id value);

The specific implementation is as follows:

  1. id objc_initWeak(id *object, id value)
  2. {
  3. *object = 0 ;
  4. return objc_storeWeak(object, value);
  5. }

The example code is converted into the compiler simulation code as follows:

  1. id obj1;
  2. objc_initWeak(&obj1, obj);

So, what is done here is to initialize obj1 to 0 (nil) first, and then pass the address of obj1 and obj as parameters to the objc_storeWeak function.

The objc_initWeak function has a prerequisite: object must be a valid pointer that has not been registered as a __weak object. And value can be null or point to a valid object.

If value is a null pointer or the object it points to has been released, object is zero-initialized. Otherwise, object will be registered as a __weak object pointing to value. This should be done by the objc_storeWeak function. The function declaration of objc_storeWeak is as follows:

  1. id objc_storeWeak(id *location, id value);

The specific implementation is as follows:

  1. id objc_storeWeak(id *location, id newObj)
  2. {
  3. id oldObj;
  4. SideTable *oldTable;
  5. SideTable *newTable;
  6.  
  7. ......
  8.  
  9. // Acquire locks for old and new values.  
  10. // Order by lock address to prevent lock ordering problems.  
  11. // Retry if the old value changes underneath us.  
  12. retry:
  13. oldObj = *location;
  14.  
  15. oldTable = SideTable::tableForPointer(oldObj);
  16. newTable = SideTable::tableForPointer(newObj);
  17.  
  18. ......
  19.  
  20. if (*location != oldObj) {
  21. OSSpinLockUnlock(lock1);
  22. # if SIDE_TABLE_STRIPE > 1  
  23. if (lock1 != lock2) OSSpinLockUnlock(lock2);
  24. #endif
  25. goto retry;
  26. }
  27.  
  28. if (oldObj) {
  29. weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
  30. }
  31. if (newObj) {
  32. newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
  33. // weak_register_no_lock returns NULL if weak store should be rejected  
  34. }
  35. // Do not set *location anywhere else. That would introduce a race.  
  36. *location = newObj;
  37.  
  38. ......
  39.  
  40. return newObj;
  41. }

Let's look at what this code does by omitting the various lock operations in the source code. Before that, let's first understand the weak table and SideTable.

The weak table is a weak reference table, implemented as a weak_table_t structure, which stores all weak reference information related to an object. Its definition is as follows (specifically defined in objc-weak.h):

  1. struct weak_table_t {
  2. weak_entry_t *weak_entries;
  3. size_t num_entries;
  4. ......
  5. };

Among them, weak_entry_t is an internal structure stored in the weak reference table, which is responsible for maintaining and storing all weak reference hash tables pointing to an object. Its definition is as follows:

  1. struct weak_entry_t {
  2. DisguisedPtr<objc_object> referent;
  3. union {
  4. struct {
  5. weak_referrer_t *referrers;
  6. uintptr_t out_of_line : 1 ;
  7. ......
  8. };
  9. struct {
  10. // out_of_line=0 is LSB of one of these (don't care which)  
  11. weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
  12. };
  13. };
  14. };

The referent is the object being referenced, i.e. the obj object in the sample code. The following union stores all weak references to the object. As can be seen from the comments, when out_of_line is equal to 0, the hash table is replaced by an array. In addition, the addresses of all weak reference objects are stored in the address of the weak_referrer_t pointer. Its definition is as follows:

typedef objc_object ** weak_referrer_t;

SideTable is a class implemented in C++. Its specific definition is in NSObject.mm. Let's take a look at the definitions of some of its member variables:

  1. class SideTable {
  2. private :
  3. static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
  4.  
  5. public :
  6.  
  7. RefcountMap refcnts;
  8. weak_table_t weak_table;
  9.  
  10. ......
  11.  
  12. }

RefcountMap refcnts, you should be able to guess what this is used for, right? It looks like a reference count or something. Haha, it seems to be, this thing stores the reference count information of an object. Of course, we will not explore it here, we are concerned about weak_table. This member variable points to the weak table of an object.

Now that we know about the weak table and SideTable, let's look back at objc_storeWeak. First, we need to find the old object pointed to by the weak pointer:

  1. oldObj = *location;

Then get the SideTable objects related to the new and old objects:

  1. oldTable = SideTable::tableForPointer(oldObj);
  2. newTable = SideTable::tableForPointer(newObj);
  3.  
  4. The next thing to do is to remove the pointing information in the weak table of the old object and create the associated information in the weak table of the new object:
  5.  
  6. if (oldObj) {
  7. weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
  8. }
  9. if (newObj) {
  10. newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
  11. // weak_register_no_lock returns NULL if weak store should be rejected  
  12. }

Next, let the weak reference pointer point to the new object:

  1. *location = newObj;

*** will return this new object:

  1. return newObj;

This is the basic implementation of objc_storeWeak. Of course, when objc_storeWeak is called in objc_initWeak, the old object is empty, so the weak_unregister_no_lock operation will not be executed.

When the object pointed to by the weak reference is released, how to deal with the weak pointer? When releasing an object, the basic process is as follows:

Calling objc_release

Because the reference count of the object is 0, dealloc is executed

In dealloc, the _objc_rootDealloc function is called

In _objc_rootDealloc, the object_dispose function is called

Calling objc_destructInstance

***Call objc_clear_deallocating

Let's focus on the last step. The specific implementation of objc_clear_deallocating is as follows:

  1. void objc_clear_deallocating(id obj)
  2. {
  3. ......
  4.  
  5. SideTable *table = SideTable::tableForPointer(obj);
  6.  
  7. // clear any weak table items  
  8. // clear extra retain count and deallocating bit  
  9. // (fixme warn or abort if extra retain count == 0?)  
  10. OSSpinLockLock(&table->slock);
  11. if (seen_weak_refs) {
  12. arr_clear_deallocating(&table->weak_table, obj);
  13. }
  14. ......
  15. }

We can see that in this function, the SideTable instance corresponding to the object is first retrieved. If the object has an associated weak reference, arr_clear_deallocating is called to clear the weak reference information of the object. Let's take a look at the specific implementation of arr_clear_deallocating:

  1. PRIVATE_EXTERN void arr_clear_deallocating(weak_table_t *weak_table, id referent) {
  2. {
  3. weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
  4. if (entry == NULL) {
  5. ......
  6. return ;
  7. }
  8. // zero out references  
  9. for ( int i = 0 ; i < entry->referrers.num_allocated; ++i) {
  10. id *referrer = entry->referrers.refs[i].referrer;
  11. if (referrer) {
  12. if (*referrer == referent) {
  13. *referrer = nil;
  14. }
  15. else   if (*referrer) {
  16. _objc_inform( "__weak variable @ %p holds %p instead of %p\n" , referrer, *referrer, referent);
  17. }
  18. }
  19. }
  20.  
  21. weak_entry_remove_no_lock(weak_table, entry);
  22. weak_table->num_weak_refs--;
  23. }
  24. }

This function first finds the weak_entry_t list corresponding to the object, and then sets the weak references to nil one by one. ***Clear the object records.

Through the above description, we can basically understand the process of a weak reference from birth to death. From this process, we can see that the processing of a weak reference involves various table lookups, additions and deletions, which still consumes a certain amount of time. Therefore, if __weak variables are used in large quantities, it will have a certain impact on performance. So, when should we use weak? The advice given to us by "Objective-C Advanced Programming" is to use the __weak modifier only when avoiding circular references.

In addition, clang also provides a lot of weak reference processing functions, such as objc_loadWeak, objc_destroyWeak, objc_moveWeak, etc. We can find relevant implementations in Apple's open source code. I will study them carefully when I have time.

refer to

"Objective-C Advanced Programming" 1.4: __weak modifier

Clang 3.7 documentation – Objective-C Automatic Reference Counting (ARC)

apple opensource – NSObject.mm

#p#

Odds and Ends

CAGradientLayer

The CAGradientLayer class is used to draw a color gradient on its background color to fill the entire shape of the layer, including rounded corners. This class inherits from the CALayer class and is very convenient to use.

Similar to the gradient processing in Quartz 2D, a gradient has a starting position (startPoint) and an ending position (endPoint). Between these two positions, we can specify a set of color values ​​(colors, elements are CGColorRef objects), which can be two or more, and each color value corresponds to a location (locations). In addition, gradients are divided into axial gradients and radial gradients.

Let's write an example to see the specific use of CAGradientLayer:

  1. CAGradientLayer * layer = [CAGradientLayer layer];
  2. layer.startPoint = (CGPoint){0.5f, 0.0f};
  3. layer.endPoint = (CGPoint){0.5f, 1.0f};
  4. layer.colors = [NSArray arrayWithObjects:(id)[UIColor blueColor].CGColor, (id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor, nil];
  5. layer.locations = @[@0.0f, @0.6f, @1.0f];
  6. layer.frame = self .view.layer.bounds;
  7.  
  8. [self.view.layer insertSublayer:layer atIndex:0];

refer to

CAGradientLayer Class Reference

Handling Ineligible Devices in Xcode

I changed to a new computer, installed Xcode 6.3, created a new certificate and profile, opened Xcode, and connected my phone. But then I found that the device was marked as Ineligible Devices and was not recognized. The situation is similar to the following picture:

The computer is trusted, and the certificates and profiles are OK. I tried restarting Xcode and reconnecting the phone several times, but it didn't work. I just can't select the device. *** I selected the device in Product->Destination. But I still can't select it in the toolbar. I'm so frustrated. Please help.

Hide the cursor of UITextField after iOS 7

After the new project only supports iOS 7, many things become much simpler, just like hiding the cursor of UITextField, with a simple sentence:

textFiled.tintColor = [UIColor clearColor];

Usually when we use UIPickerView as our UITextField's inputView, we need to hide the cursor. Of course, if you want to change the cursor color, you can do the same.

There is a legacy problem with this approach: usually when we use UIPickerView as the inputView of UITextField, we don't want to perform various menu operations (select all, copy, paste), but when we just want to set the tintColor of UITextField, we can still perform these operations, so additional processing is required. We can handle this problem like this: in textFieldShouldBeginEditing:, we set the userInteractionEnabled of UITextField to NO, and then in textFieldShouldEndEditing:, set this value back. As follows:

  1. - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
  2.  
  3. textField.userInteractionEnabled = NO;
  4.  
  5. return YES;
  6. }
  7.  
  8. - (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
  9.  
  10. textField.userInteractionEnabled = YES;
  11.  
  12. return YES;
  13. }

That's it. Of course, this is just one way we are currently using. There are other methods, just google or stackoverflow.

Left alignment issue of Chinese text in UIAlertView after iOS 7

Before iOS 7, if we want to display the text in UIAlertView on the left, we can use the following code to handle it:

  1. for (UIView *view in alert.subviews) {
  2. if ([[view class ] isSubclassOfClass:[UILabel class ]]) {
  3. ((UILabel*)view).textAlignment = NSTextAlignmentLeft;
  4. }
  5. }

But unfortunately, after iOS 7, Apple doesn't allow us to do this. When we get the subviews of UIAlertView, we get an empty array and we can't get the label we want. What should we do? Three ways: tell the product manager and UED that this can't be implemented (of course, this will be despised, and people will say you are incompetent); write it yourself; find a third-party open source code. Hehe, but because of the tight schedule recently, I decided to tell them that it can't be implemented, haha. But I found an open source on github, Custom iOS AlertView, with a lot of stars. It looks good, I will study it carefully later.

<<:  What do you think about vocational college students giving up their 400,000 yuan annual salary to return to school to take the postgraduate entrance examination?

>>:  Summary of Swift basic grammar learning

Recommend

A complete analysis of social media methods for App operation and promotion!

In the era of mobile Internet, traffic is king. I...

Facebook advertising targeting optimization uses these 8 methods!

The number of advertisers and industries in China...

How APP ranking manipulation steals iTunes accounts and the harm it causes to CPs

Why do apps steal real users’ iTunes accounts to ...

Testin crash analysis: one line of code allows you to easily find bugs

For mobile developers, when an application crashe...

A universal formula for user growth

I have always believed that no matter what you do...

5 ways to acquire 10 million users at low cost using mini programs

In the first quarter of 2017, the number of WeCha...

Rules for Creating Brand Hot Products in 2022

In a homogeneous market, how can brands find diff...

Analysis of Keep’s model centered on user growth!

The author of this article conducts a detailed an...

Keep brand marketing promotion model!

In recent years, I have found that vertical Inter...

Ramen says, the four-step traffic method behind explosive growth!

"In 2017, we started content marketing; in 2...