Behavior in iOS apps

In order to avoid tearing, I declare in advance: This article is purely translation, just for learning, plus the level is limited, forgive me!

【原文】https://www.objc.io/issues/13-architecture/singletons/

Behavior in iOS apps - Krzysztof Zabłocki

As developers, we strive to write clean and well-organized code. There are many patterns that we can use to achieve this, the best of which is the composition pattern. Composition makes it easier to follow the Single Responsibility Principle ( Single Responsibility Principe) and simplify class files.

Instead of having a Massive View Controller serving different roles (such as data sources and delegates) at the same time, you can separate these roles into different class files. View controllers are only responsible for configuring them and coordinating work. After all, the less code you write, the less code you need to debug and maintain.

So what exactly is behavior?

A behavior is an object responsible for implementing a specific character, for example, you could have a behavior that implements a parallax animation.

The behavior in this post will take advantage of Interface Builderlimiting the amount of code while effectively collaborating with non-coders. However, if you don't use Interface Builderyou can also use behaviors and still get most of the benefits.

Many behaviors do not require any additional code besides setting, and you can Interface Buildercomplete these settings through and code (the same setting method). In many cases, you don't even need to reference them with properties.

Why use behavior?

Many iOS projects end up being Massive View Controller classes because people put 80% of the logic in the app here. This is a serious problem, because try controllers are the least reused parts of our code, and they are hard to test and maintain.

Behavior can help us avoid this situation, so what benefits does it bring us?

Lighter View Controller

Using behaviors means you can move a lot of code out of the controller into separate class files. If you use behaviors, you usually form a lightweight view controller. For example, mine are usually less than 100 lines of code.

code reuse

Because a behavior is only responsible for one role, it is easy to avoid logical dependencies between a specific behavior and a specific application. This allows you to share scents between different apps.

Testability

Behaviors are small classes that work like black boxes. This means they are easily covered by unit tests. You don't even need to create real views to test them just by providing mock objects.

Ability to let non-coders modify application logic

If we decide to use Interface Builderexploit behavior, we can teach designers how to modify application logic. Designers can add, remove behaviors, and modify parameters without any knowledge of Objective-C.

This is a great benefit for workflows, especially small teams.

How to create flexible behavior?

Behaviors are simple objects that don't require any special code, but here are a few concepts that can really help make them easier to use and more powerful.

runtime properties

A lot of developers ignore it Interface Builder, don't even learn it, and again, often miss its true power.

Runtime properties are Interface Builderone of the key features used. They give you a way to set custom classes that can set properties for iOS's built-in classes. For example, have you set the layer's corner radius? You can Interface Buildersimply set runtime properties for it directly in:

Interface Builder When creating behaviors in , you intend to rely heavily on runtime properties to set behavior options. As a result, there are often more runtime properties here:

behavioral survival time

If an object is removed from Interface Buildercreation, it is immediately created and removed unless another object holds a strong reference to it. However, this is not perfect for behavior, since it needs to live as long as the view controller it acts on.

You can create a property in the view controller and strongly reference the behavior, but this is not perfect for several reasons:

  • After creating and configuring behaviors, you don't need to interact with many behaviors.
  • Creating properties just to keep the object alive is cumbersome.
  • If you need to remove specific behavior, you need to clean up those unused properties.

Using the Objective-C runtime to invert time-to-live bindings

Instead of manually setting a strong reference to the behavior in the view controller, we can treat the behavior as an associated object of the view as part of the configuration process, if desired.

This means that if we need to remove a specific behavior in a certain situation, we only need to remove the code or Interface Builderobject that configures that behavior, and no additional changes are required.

This can be achieved as follows:

@interface KZBehavior: UIControl
@property(nonatomic, weak) IBOutlet id owner;
@end

@implementation KZBehavior
- (void)setOwner:(id)owner {
	if(_owner != owner) {
		[self releaseLifetimeFromObject:_owner];
		_owner = owner;
		[self bindLifetimeToObject:_owner];
	}
}

- (void)bindLifetimeToObject:(id)object {
	objc_setAssociatedObject(object, (__bridge void *)self, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)releaseLifetimeFromObject:(id)object {
	objc_setAssociatedObject(object, (__bridge void *), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

Here we make use of an association object to create a strong reference to a specific owner object.

behavioral events

It's useful to have actions publish events, for example, when an animation ends. This feature can be enabled by UIControlinheriting the behavior from Interface Builder. Then, specific behaviors can be invoked:

[self sendActionsForControlEvents:UIControlEventValueChanged];

This allows you to hook up behavior to your view controller's code.

An example of a simple behavior

So what is easiest to implement as behavior?

Here is a simple way to add parallax animation to UIViewController class (not custom class):

video

Ever choose a picture from your photo library and camera?

video

more advanced features

The above behavior is simple, but when we need more advanced features, what should shiitake do? A behavior can be as powerful as you want it to be, let's look at a few more complex examples.

If the behavior requires several delegates like UIScrollViewDelegate, you will soon see that you can only have at most one behavior on a given screen. However, we can handle this by implementing an object that simply multiplexes the proxy:

@interface MutiplexerProxyBehavior: KZBehavior
@property(nonatomic, strong) IBOutletCollection(id) NSArray *targets;
@end

@implementation MutiplexerProxyBehavior
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
	NSMethodSignature *sig = [super methodSignatureForSelector:sel];
	if(!sig) {
		for(id obj in self.targets) {
			if((sig = [obj methodSignatureForSelector:sel)) {
				break;
			}
		}
	}
	return sig;
}
- (BOOL)respondsToSelector:(SEL)aSelector {
	BOOL base = [super respondsToSelector:aSelector];
	if(base) {
		return base;
	}
	return [self.targets.firstObject respondsToSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
	for(id obj in self.targets) {
		if([obj respondsToSelector:anInvocation.selector]) {
			[anInvocation invokeWithTarget:obj];
		}
	}
}
@end

By creating an instance of the multiway behavior, you can designate it as a delegate for the scroll view (or any object with a delegate), so that delegate calls will be forwarded to everyone.

in conclusion

Behaviors are an interesting concept that can simplify your codebase, allowing you to reuse a lot of code across different applications. They will also allow you to collaborate effectively with non-coders on your team by fine-tuning or modifying the behavior in your application.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325388164&siteId=291194637