In order to avoid a quarrel, advance statement: this article is pure translation, just for learning, plus limited level, forgive me!

[text] 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 we can use for this purpose, and one of the best is composition. Composition makes it easier for us to follow the Single Responsibility Principe ** and simplify class files.

Unlike Massive View controllers that serve different roles (such as data sources and delegates), you can separate these roles into different class files. The view controller is only responsible for configuring them and coordinating their work. After all, the less code you write, the less code you have to debug and maintain.

So what exactly is behavior?

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

The behavior in this article will leverage Interface Builder to limit the amount of code while effectively collaborating with non-coders. However, if you don’t use Interface Builder you can use behavior and still get most of the benefits.

Many of these actions don’t require any extra code other than setup, which you can do with Interface Builder and code (the same setup method). In many cases, you don’t even need to reference them with attributes.

Why use behavior?

A lot of iOS projects end up focusing on Massive View Controller classes because people put 80% of the logic in their apps there. This is a serious problem because the attempt controllers are the least repeatable part of our code, making them difficult to test and maintain.

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

Lighter View Controller

Usage behavior means that you can move a lot of code from the controller into separate class files. If you use behaviors, you usually end up with a lightweight view controller. For example, mine are usually less than 100 lines of code.

Code reuse

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

testability

Behavior is a small class that works like a black box. This means they are easily covered by unit tests. You don’t even have to create real views just provide mock objects to test them.

The ability for non-coders to modify application logic

If we decide to use Interface Builder to leverage behavior, we can teach designers how to modify application logic. Designers can add and remove behaviors and modify parameters without having to know anything about Objective-C.

This is a big benefit for workflow, especially for small teams.

How do you create flexible behavior?

Behavior is a simple object that doesn’t require any special code, but there are a few concepts that can really help make them more usable and more powerful.

Runtime properties

Many developers ignore Interface Builder, don’t even learn about it, and often miss its true power.

Runtime properties are one of the key features of using Interface Builder. They give you a way to set up custom classes that can set properties for built-in classes in iOS. For example, have you set the corner radius of the layer? You can simply set the runtime properties for it directly in Interface Builder:

Interface Builder

The survival time of the behavior

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

You can create a property in the view controller and strong-reference the behavior, but this isn’t perfect for several reasons:

  • When behaviors are created and configured, you don’t have to interact with many behaviors.
  • Creating attributes just to keep the object alive is cumbersome.
  • If you need to remove specific behaviors, you need to clean up unused attributes.

Use the Objective-C runtime to reverse the lifetime binding

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 necessary.

This means that in some cases if we need to remove a particular behavior, we just need to remove the code or Interface Builder object that configured the behavior, and no additional changes are required.

This can be done 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; [selfbindLifetimeToObject:_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
Copy the code

Here we use the association object to create a strong reference to the specific owner object.

Behavior event

It’s useful to have behaviors that publish events, for example, when an animation ends. You can enable this functionality in Interface Builder by inheriting behavior from UIControl. The specific behavior can then be called:

[self sendActionsForControlEvents:UIControlEventValueChanged];
Copy the code

This allows you to connect the behavior to the view controller’s code.

An example of a simple behavior

So what is the easiest thing to implement as behavior?

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

video

Ever select an image from your photo library and camera?

video

More advanced features

The above behavior is simple, but when we need more advanced characteristics, how should shiitake do it? Behavior can be as powerful as you want it to be, but let’s look at a few more complicated examples.

If the behavior requires several delegates like UIScrollViewDelegate, you’ll soon see that you can only have at most one behavior on a given interface. However, we can deal with this problem by implementing an object that simply multiplexes:

@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
Copy the code

By creating an instance of the multiplexing behavior, you can specify it as a delegate to the scroll view (or any object that has a delegate) so that the delegate call will be forwarded to everyone.

conclusion

Behavior is an interesting concept that can simplify your code base and allow you to reuse a lot of code across different applications. They also allow you to collaborate effectively with non-coders on your team by fine-tuning or modifying the behavior in your application.