Ry’s Objective-C Tutorial---Methods

RyPress - Quality Software Tutorials

You’re reading Ry’s Objective-C Tutorial

Methods

Methods represent the actions that an object knows how to perform. They’re the logical counterpart to properties, which represent an object’s data. You can think of methods as functions that are attached to an object; however, they have a very different syntax.

In this module, we’ll explore Objective-C’s method naming conventions, which can be frustrating for experienced developers coming from C++, Java, Python, and similar languages. We’ll also briefly discuss Objective-C’s access modifiers (or lack thereof), and we’ll learn how to refer to methods using selectors.

Naming Conventions

Objective-C methods are designed to remove all ambiguities from an API. As a result, method names are horribly verbose, but undeniably descriptive. Accomplishing this boils down to three simple rules for naming Objective-C methods:

  1. Don’t abbreviate anything.
  2. Explicitly state parameter names in the method itself.
  3. Explicitly describe the return value of the method.

Keep these rules in mind as you read through the following exemplary interface for a Car class.

// Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject

// Accessors
- (BOOL)isRunning;
- (void)setRunning:(BOOL)running;
- (NSString *)model;
- (void)setModel:(NSString *)model;

// Calculated values
- (double)maximumSpeed;
- (double)maximumSpeedUsingLocale:(NSLocale *)locale;

// Action methods
- (void)startEngine;
- (void)driveForDistance:(double)theDistance;
- (void)driveFromOrigin:(id)theOrigin toDestination:(id)theDestination;
- (void)turnByAngle:(double)theAngle;
- (void)turnToAngle:(double)theAngle;

// Error handling methods
- (BOOL)loadPassenger:(id)aPassenger error:(NSError **)error;

// Constructor methods
- (id)initWithModel:(NSString *)aModel;
- (id)initWithModel:(NSString *)aModel mileage:(double)theMileage;

// Comparison methods
- (BOOL)isEqualToCar:(Car *)anotherCar;
- (Car *)fasterCar:(Car *)anotherCar;
- (Car *)slowerCar:(Car *)anotherCar;

// Factory methods
+ (Car *)car;
+ (Car *)carWithModel:(NSString *)aModel;
+ (Car *)carWithModel:(NSString *)aModel mileage:(double)theMileage;

// Singleton methods
+ (Car *)sharedCar;

@end

Abbreviations

The easiest way to make methods understandable and predictable is to simply avoid abbreviations. Most Objective-C programmer expectmethods to be written out in full, as this is the convention for all of the standard frameworks, from Foundation to UIKit. This is why the above interface chose maximumSpeed over the more concise maxSpeed.

Parameters

One of the clearest examples of Objective-C’s verbose design philosophy is in the naming conventions for method parameters. Whereas C++, Java, and other Simula-style languages treat a method as a separate entity from its parameters, an Objective-C method name actually contains the names of all its parameters.

For example, to make a Car turn by 90 degrees in C++, you would call something like turn(90). But, Objective-C finds this too ambiguous. It’s not clear what kind of argument turn() should take—it could be the new orientation, or it could be an angle by which to increment your current orientation. Objective-C methods make this explicit by describing the argument with a preposition. The resulting API ensures the method will never be misinterpreted: it’s either turnByAngle:90 or turnToAngle:90.

When a method accepts more than one parameter, the name of each argument is also included in the method name. For instance, the aboveinitWithModel:mileage: method explicitly labels both the Modelargument and the mileage argument. As we’ll see in a moment, this makes for very informative method invocations.

Return Values

You’ll also notice that any methods returning a value explicitly state what that value is. Sometimes this is as simple as stating the class of the return type, but other times you’ll need to prefix it with a descriptive adjective.

For example, the factory methods start with car, which clearly states that the method will return an instance of the Car class. The comparison methods fasterCar: and slowerCar: return the faster/slower of the receiver and the argument, and this is also clearly expressed in the API. It’s worth noting that singleton methods should also follow this pattern (e.g., sharedCar), since the conventional instance method name is ambiguous.

For more information about naming conventions, please visit the official Cocoa Coding Guidlines.

Calling Methods

As discussed in the Instantiation and Usage section, you invoke a method by placing the object and the desired method in square brackets, separated by a space. Arguments are separated from the method name using a colon:

[porsche initWithModel:@"Porsche"];

When you have more than one parameter, it comes after the initial argument, following the same pattern. Each parameter is paired with a label, separated from other arguments by a space, and set off by a colon:

[porsche initWithModel:@"Porsche" mileage:42000.0];

It’s a lot easier to see the purpose behind the above naming conventions when you approach it from an invocation perspective. They make method calls read more like a human language than a computer one. For example, compare the following method call from Simula-style languages to Objective-C’s version:

// Python/Java/C++
porsche.drive("Home", "Airport");

// Objective-C
[porsche driveFromOrigin:@"Home" toDestination:@"Airport"];

It might be more to type, but that’s why Xcode comes with such a nice auto-completion feature. You’ll appreciate the verbosity when you leave your code for a few months and come back to fix a bug. This clarity also makes it much easier to work with third-party libraries and to maintain large code bases.

Nested Method Calls

Nesting method calls is a common pattern in Objective-C programs. It’s a natural way to pass the result of one call to another. Conceptually, it’s the exact same as chaining methods, but the square-bracket syntax makes them look a little bit different:

// JavaScript
Car.alloc().init()

// Objective-C
[[Car alloc] init];

First, the [Car alloc] method is invoked, then the init method is called on its return value.

Protected and Private Methods

There are no protected or private access modifiers for Objective-C methods—they are all public. However, Objective-C does provide alternative organizational paradigms that let you emulate these features.

“Private” methods can be created by defining them in a class’s implementation file while omitting them from its interface file. Since other objects (including subclasses) are never supposed to import the implementation, these methods are effectively hidden from everything but the class itself.

In lieu of protected methods, Objective-C provides categories, which are a more general solution for isolating portions of an API. A full example can be found in “Protected” Methods.

Selectors

Selectors are Objective-C’s internal representation of a method name. They let you treat a method as an independent entity, enabling you to separate an action from the object that needs to perform it. This is the basis of the target-action design pattern, which is introduced in the Interface Builder chapter of Ry’s Cocoa Tutorial. It’s also an integral part of Objective-C’s dynamic typing system.

There are two ways to get the selector for a method name. The@selector() directive lets you convert a source-code method name to a selector, and the NSSelectorFromString() function lets you convert a string to a selector (the latter is not as efficient). Both of these return a special data type for selectors called SEL. You can use SEL the exact same way as BOOLint, or any other data type.

// main.m
#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *porsche = [[Car alloc] init];
        porsche.model = @"Porsche 911 Carrera";
        
        SEL stepOne = NSSelectorFromString(@"startEngine");
        SEL stepTwo = @selector(driveForDistance:);
        SEL stepThree = @selector(turnByAngle:quickly:);
        
        // This is the same as:
        // [porsche startEngine];
        [porsche performSelector:stepOne];

        // This is the same as:
        // [porsche driveForDistance:[NSNumber numberWithDouble:5.7]];
        [porsche performSelector:stepTwo
                      withObject:[NSNumber numberWithDouble:5.7]];
        
        if ([porsche respondsToSelector:stepThree]) {
            // This is the same as:
            // [porsche turnByAngle:[NSNumber numberWithDouble:90.0]
            //              quickly:[NSNumber numberWithBool:YES]];
            [porsche performSelector:stepThree
                          withObject:[NSNumber numberWithDouble:90.0]
                          withObject:[NSNumber numberWithBool:YES]];
        }
        NSLog(@"Step one: %@", NSStringFromSelector(stepOne));
    }
    return 0;
}

Selectors can be executed on an arbitrary object via performSelector:and related methods. The withObject: versions let you pass an argument (or two) to the method, but require those arguments to be objects. If this is too limiting for your needs, please see NSInvocationfor advanced usage. When you’re not sure if the target object defines the method, you should use the respondsToSelector: check before trying to perform the selector.

The technical name for a method is the primary method name concatenated with all of its parameter labels, separated by colons. This makes colons an integral aspect of method names, which can be confusing to Objective-C beginners. Their usage can be summed up as follows: parameterless methods never contain a colon, while methods that take a parameter always end in a colon.

A sample interface and implementation for the above Car class is included below. Notice how we have to use NSNumber instead of doublefor the parameter types, since the performSelector:withObject:method doesn’t let you pass primitive C data types.

// Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject

@property (copy) NSString *model;

- (void)startEngine;
- (void)driveForDistance:(NSNumber *)theDistance;
- (void)turnByAngle:(NSNumber *)theAngle
            quickly:(NSNumber *)useParkingBrake;

@end
// Car.m
#import "Car.h"

@implementation Car

@synthesize model = _model;

- (void)startEngine {
    NSLog(@"Starting the %@'s engine", _model);
}

- (void)driveForDistance:(NSNumber *)theDistance {
    NSLog(@"The %@ just drove %0.1f miles",
          _model, [theDistance doubleValue]);
}

- (void)turnByAngle:(NSNumber *)theAngle
            quickly:(NSNumber *)useParkingBrake {
    if ([useParkingBrake boolValue]) {
        NSLog(@"The %@ is drifting around the corner!", _model);
    } else {
        NSLog(@"The %@ is making a gentle %0.1f degree turn",
              _model, [theAngle doubleValue]);
    }
}

@end

Summary

This module explained the reasoning behind Objective-C’s method naming conventions. We also learned that there are no access modifiers for Objective-C methods, and how to use @selector to dynamically invoke methods.

Adapting to new conventions can be a frustrating process, and the dramatic syntactic differences between Objective-C and other OOP languages won’t make your life any easier. Instead of forcing Objective-C into your existing mental model of the programming universe, it helps to approach it in its own right. Try designing a few simple programs before passing judgement on Objective-C’s verbose philosophy.

That covers the basics of object-oriented programming in Objective-C. The rest of this tutorial explores more advanced ways to organize your code. First on the list are protocols, which let you share an API between several classes.

Mailing List

Sign up for my low-volume mailing list to find out when new content is released. Next up is a comprehensive Swift tutorial planned for late January.

Email Address:

You’ll only receive emails when new tutorials are released, and your contact information will never be shared with third parties. Click here to unsubscribe.

内容概要:本文档详细介绍了一个基于MATLAB实现的跨尺度注意力机制(CSA)结合Transformer编码器的多变量时间序列预测项目。项目旨在精准捕捉多尺度时间序列特征,提升多变量时间序列的预测性能,降低模型计算复杂度与训练时间,增强模型的解释性和可视化能力。通过跨尺度注意力机制,模型可以同时捕获局部细节和全局趋势,显著提升预测精度和泛化能力。文档还探讨了项目面临的挑战,如多尺度特征融合、多变量复杂依赖关系、计算资源瓶颈等问题,并提出了相应的解决方案。此外,项目模型架构包括跨尺度注意力机制模块、Transformer编码器层和输出预测层,文档最后提供了部分MATLAB代码示例。 适合人群:具备一定编程基础,尤其是熟悉MATLAB和深度学习的科研人员、工程师和研究生。 使用场景及目标:①需要处理多变量、多尺度时间序列数据的研究和应用场景,如金融市场分析、气象预测、工业设备监控、交通流量预测等;②希望深入了解跨尺度注意力机制和Transformer编码器在时间序列预测中的应用;③希望通过MATLAB实现高效的多变量时间序列预测模型,提升预测精度和模型解释性。 其他说明:此项目不仅提供了一种新的技术路径来处理复杂的时间序列数据,还推动了多领域多变量时间序列应用的创新。文档中的代码示例和详细的模型描述有助于读者快速理解和复现该项目,促进学术和技术交流。建议读者在实践中结合自己的数据集进行调试和优化,以达到最佳的预测效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值