Welcome to follow my official account. I will regularly share some solutions to the problems I encounter in the project and some practical skills of iOS. At the present stage, I will mainly sort out some basic knowledge and record it

The post will also be synchronized to my blog: ppsheep.com

This article mainly explains some principles of OC. It may be a bit boring, but when you really understand it, you will feel suddenly enlightened. There will be objects, properties, messages and some introduction to the runtime, until we really understand these principles, our development level will be further improved, rather than stop at the simple writing of the view, to write well, also need to understand these principles.

Understand the concept of a property

Before we start talking about attributes, let’s understand a few concepts:

  • Objects: Objects are the “basic building blocks”, and in OC we often use objects to store and pass data
  • Messaging: The process of passing data between objects and performing tasks is called messaging
  • OC Runtime: When the application is running, the code that supports it is called Runtime, which provides functions for passing data between objects and contains all the logic needed to create class instances

The above three concepts are particularly important in OC programming, and although you may not understand them very well now, you will certainly understand them as you learn more.

attribute

“Property” is the instance variable used to store the data of an object in an OC. Instance variables are generally accessed through “access method” and “set method” to set instance variables. As we have said before, for instance variables, it is best to read them directly if they are accessed by themselves (underlined directly), and to set them using properties. See the previous article (iOS- Effectively writing High-quality Objective-C Methods – II) for more details.

I’m going to skip the simple conceptual stuff and just give the conclusion:

  • All instance variables do not have to be defined in the interface or declaration file, but can be defined in the implementation file to protect the internal information related to the class implementation
  • Properties are named in a standard way and access methods are added automatically during compilation by the compiler

Next, let’s talk about a few keywords:

@synthesize

We can use this keyword in our code to specify the instance variable we want

For example: in a header file

@interface : NSObject

@property(nonatomic, copy) NSString *name;

@endCopy the code

This property, in our runtime environment, generates the instance variable _name, but we don’t want to use this name in.m, so we can write this in our implementation file:

@implementation

@synthesize name =  _myName

@endCopy the code

So we can use _myName directly in the.m implementation file to manipulate the property name

However, for the sake of written specifications and collaboration between teams, I recommend that code be written in the OC code style of the specification so that team members can see the code at a glance

@dynamic

This keyword is used to prevent the compiler from automatically synthesizing access methods, but I rarely use this keyword, and the above keyword is also rarely used.

This keyword means: prevent the compiler from synthesizing the instance variable corresponding to the attribute, and do not synthesize the instance variable access method

The instance variable is the underscore _name and the property is the name declared through the property

The two need to be distinguished

Attribute characteristics

nonatomic

Attribute attributes are the keywords in the property parentheses when we declare a property

@property(nonatomic, readwrite, copy);Copy the code

Nonatomic, readwrite, copy these are all attributes, so let’s start with nonatomic

This keyword is called attribute atomicity, and in layman’s terms, this keyword is used to control the synchronization locking of attributes. Synchronization locks: If the property is declared by atomic, the two threads will always be able to read the valid value of the property. If the property is declared by nonatomic, the two threads will read the valid value of the property. If a thread is modifying the value of the property and another thread is reading the value of the property, it is possible to read the value of the property that has not yet been modified (here is the value of the property that has not been modified, it is possible to read the value of the property that has no meaning at all).

Again, why do we always see properties declared using nonatomic when writing iOS applications? It’s because of the overhead of using synchronous locks in iOS, which can cause performance problems. In general, we do not require attributes must have atomicity, because the atomic is not to say is “thread safety”, if we need to implement a thread safe, so also need to use more at the bottom of the synchronous locking mechanism, even if is the use of atomic to declare, different threads or can read different attribute values, It’s just saying that the attribute value is valid and meaningful.

So we still use nonaomic when developing iOS applications, but we don’t encounter this performance bottleneck in macOS development, because the performance configuration is different

readwrite/readonly

The property attribute, as we can see from the literal, is the declaration of property permissions, there is no more to say about this.

strong/copy/assign/weak

This may be the one we use the most and think about the most. In fact, we know how to use it at ordinary times, but why do we use it like this?

Assign: “setup methods” will only assign to “scalar types”, such as CGFloat and NSInteger

Strong: This property represents an ownership relationship. In setting methods, this property preserves the new value, releases the old value, and then sets the new value

Copy: It’s a little bit like strong, but it doesn’t keep the new value, it just copies the new value, and we use this a lot when we have NSString, so why do we use this a lot in NSString, because when we set, We might pass in an NSMutableString object, which is a subclass of NSString, that can be assigned to NSString, and if we don’t copy, then when we externally change the value of NSMultableString, our property value will be changed directly, so in this case, We need a copy that’s immutable

Weak: This is a weak reference, so when we set this method, we don’t keep the new value, we don’t release the old value, and when the object to which the property refers is destroyed, the property value is also cleared, and we often use weak when we define a view in a ViewController, but we always declare the view as strong, Of course, this doesn’t have a big impact, but our application generates a lot of useless view property values that are not released and occupy invalid memory. So I recommend that you declare the view as weak when you use it

@property(nonatomic,weak) UILable *lable; UILable * lable = [[UILable alloc] init]; [self.view addSubview: lable]; self.lable = lable;Copy the code

The method name

When we define a property as a Boolean, our convention is to get the method, usually starting with “is”, so we can declare it that way

@property(nonatomic,getter=isOn) Bool on;Copy the code

The method of getting the property is isOn;

We define attribute attributes in attributes, so when we write assignments, we should assign strictly to attribute attributes. For example, we have an initial method that needs to assign to our attribute NSString Name

- (instancetype)initWithName:(NSString *)name{if(self = [super init]){_name = [name copy]; }}Copy the code

Hide implementation details with “family pattern”

A class family is a useful pattern for hiding the implementation details behind an abstract base class. So what is a family of classes?

Here’s an example:

There’s a class called UIButton in UIKit, and if we want to create a button, we can call a class method

+ (UIButton *)buttonWithType:(UIButtonType)type;Copy the code

The object returned by this method depends on the type of the button passed in. However, regardless of the type passed in, the returned class inherits from the same base class: UIButton. Thus, all classes that inherit from UIButton form a family of classes.

In the system framework, many classes are used.

So why do it? So UIButton, when you implement it, the user doesn’t have to worry about what class the button that you’re creating is in, or how you’re drawing the button, all I need to know is, how do I create the button, how do I set the title. How to add click operations etc.

Create the class family

Let’s now imagine a class that handles employees, each with its own name and salary, and a manager that can command it to perform routine tasks. However, the work content of various employees is different. When leading employees to do a project, the manager does not need to care about how each employee completes his or her own work, but only needs to instruct them to start.

First we define a base class:

typedef NS_ENUM(NSUinteger, PPSEmployeeType){ PPSEmployeeTypeDeveloper, PPSEmployeeTypeDesigner, PPSEmployeeTypeFinance, } @interface PPSEmployee : NSObject @property (nonatomic, copy) NSStirng *name; @property (nonamotic, assign) NSUInteger salary; // create method + (PPSEmployee *)employeeWithType:(PPSEmployeeType) type; - (void)doADaysWork; @endCopy the code
@implementation PPSEmployee + (PPSEmployee *)employeeWithType:(PPSEmployeeType) type{ switch (type){ case PPSEmployeeTypeDeveloper: return [PPSEmployeeDeveloper new]; break; case PPSEmployeeTypeDesigner: return [PPSEmployeeDesigner new]; break; case PPSEmployeeTypeFinance: return [PPSEmployeeFinance new]; break; }} - (void)doADaysWork{// subclass} @endCopy the code

Each entity subclass inherits from the base class

@interface PPSEmployeeDeveloper    :    PPSEmployee
@endCopy the code
@implementation PPSEmployeeDeveloper

- (void)doADaysWork{
    [self coding];
}

@endCopy the code

The way we’ve implemented it above is by creating instances of employee classes based on their categories, which is essentially a factory pattern. In Java, we know that this is usually done through abstract classes, but OC has no such thing, so developers usually document how to use it.

There are families of classes in Cocoa and most of the collection types are families of classes and we don’t get what we want when we try to determine whether an object belongs to a particular class:

id myArr = @[@"a",@"b"]; If ([myArr class] == [NSArray class]){// this will never run because we know that [myArr class] always returns a subclass of NSArr, NSArray is just a base class}Copy the code

Of course, when we do this, we should all know that we’re using isKindOfClass, which is the same class, not the same class

Use associated objects to store custom data in existing classes

Sometimes we need to store information in an object, and the best way to do that is to create a subclass and then use the subclass. However, this is not always possible, and sometimes instances of classes may be created by some mechanism, and we developers have no way to create instances of our own subclasses. Fortunately, we can solve this problem with a powerful feature of OC called relational objects.

So what are the things associated with the object?

We can associate one object with many other objects, which are distinguished by “keys”. When you store values, you can maintain memory by specifying a storage policy, and the storage policy is you’re storing an NSString, so you should change your storage policy to copy, something like that

Here are the object association types:

Association types Equivalent @property
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic,copy
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
OBJC_ASSOCIATION_COPY copy
OBJC_ASSOCIATION_RETAIN retain

Related methods for managing associated objects:

  • Void objc_ setAssociatedObject(ID object, void *key, ID value, objc_AssociationPolicy Policy) Sets the associated object for an object with the given key and policy
  • Void objc _ getAssociatedObject(ID object, void *key) getAssociatedObject(id object, void *key) getAssociatedObject(id object, void *key) getAssociatedObject(id object, void *key
  • Void objc_removeAssociatedObject(ID object) Use this method to remove all associated objects of a specified object

Using this approach, we can think of an object as an NSDictionary and think of the values associated with it as entries in the dictionary. Then these associated objects are equivalent to setting the values in the dictionary and getting the values in the dictionary

Using an example

In iOS, if we want to use UIAlertView we need to define it like this

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@" Are you sure? "Message :@" delegate:self cancelButtonTitle:@" Cancel" otherButtonTitles:@" continue ", nil]; [alert show];Copy the code

And then we need to implement the UIAlertView agent to do the identification of the operation

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ if (buttonIndex == 0) { [self doCancle]; }else{ [self doContinue]; }}Copy the code

Now, that’s fine, but if we need to handle multiple warnings in the current class, the code gets complicated, and we need to determine the current UIAlertView’s message in the delegate, and do different things based on that message.

If we can do that when we create UIAlertView, then we don’t need to judge UIAlertView in the delegate. In fact, it works.

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@" Are you sure? "Message :@" delegate:self cancelButtonTitle:@" Cancel" otherButtonTitles:@" continue ", nil]; void (^block) (NSInteger) = ^(NSInteger buttonIndex){ if (buttonIndex == 0) { NSLog(@"cancle"); }else{ NSLog(@"continue"); }}; // Set block to UIAlertView's associated object objc_setAssociatedObject(alert, PPSMyAlertViewKey, block, OBJC_ASSOCIATION_COPY); [alert show];Copy the code

We just need to get the block in the delegate

 void (^block)(NSInteger) =  objc_getAssociatedObject(alertView, PPSMyAlertViewKey);
 block(buttonIndex);Copy the code