Ry’s Objective-C Tutorial---Classes

Classes

As in many other object-oriented programming language, Objective-C classes provide the blueprint for creating objects. First, you define a reusable set of properties and behaviors inside of a class. Then, you instantiate objects from that class to interact with those properties and behaviors.

Objective-C is similar to C++ in that it abstracts a class’s interface from its implementation. An interface declares the public properties and methods of a class, and the corresponding implementation defines the code that actually makes these properties and methods work. This is the same separation of concerns that we saw with functions.

A class’s interface and implementation

In this module, we’ll explore the basic syntax for class interfaces, implementations, properties, and methods, as well as the canonical way to instantiate objects. We’ll also introduce some of Objective-C’s introspection and reflection capabilities.

Creating Classes

We’ll be working with a class called Car whose interface resides in a file named Car.h (also called a “header”) and whose implementation resides in Car.m. These are the standard file extensions for Objective-C classes. The class’s header file is what other classes use when they need to interact with it, and its implementation file is used only by the compiler.

Xcode provides a convenient template for creating new classes. To create our Car class, navigate to File > New > File… or use the Cmd+Nshortcut. For the template, choose Objective-C class under the iOS > Cocoa Touch category. After that, you’ll be presented with some configuration options:

Creating the  Car class

Use Car for the Class field and NSObject for the Subclass of field. NSObject is Objective-C’s top-level class from which almost all others inherit. Clicking Next will prompt you to select a location for the file. Save it in the top-level of the project directory. At the bottom of that dialog, make sure that your project is checked in the Targets section. This is what actually adds the class to the list of compiled sources.

Adding the class to the build target

After clicking Next, you should see new Car.h and Car.m files in Xcode’s Project Navigator. If you select the project name in the navigator, you’ll also find Car.m in the Build Phases tab under the Compile Sourcessection. Any files that you want the compiler to see must be in this list (if you didn’t create your source files through Xcode, this is where you can manually add them).

The list of compiled source files

Interfaces

Car.h contains some template code, but let’s go ahead and change it to the following. This declares a property called model and a method called drive.

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

@interface Car : NSObject {
    // Protected instance variables (not recommended)
}

@property (copy) NSString *model;

- (void)drive;

@end

An interface is created with the @interface directive, after which come the class and the superclass name, separated by a colon. Protected variables can be defined inside of the curly braces, but most developers treat instance variables as implementation details and prefer to store them in the .m file instead of the interface.

The @property directive declares a public property, and the (copy)attribute defines its memory management behavior. In this case, the value assigned to model will be stored as a copy instead of a direct pointer. The Properties module discusses this in more detail. Next come the property’s data type and name, just like a normal variable declaration.

The -(void)drive line declares a method called drive that takes no parameters, and the (void) portion defines its return type. The minus sign prepended to the method marks it as an instance method (opposed to a class method).

Implementations

The first thing any class implementation needs to do is import its corresponding interface. The @implementation directive is similar to @interface, except you don’t need to include the super class. Private instance variables can be stored between curly braces after the class name:

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

@implementation Car {
    // Private instance variables
    double _odometer;
}

@synthesize model = _model;    // Optional for Xcode 4.4+

- (void)drive {
    NSLog(@"Driving a %@. Vrooooom!", self.model);
}

@end

@synthesize is a convenience directive that automatically generates accessor methods for the property. By default, the getter is simply the property name (model), and the setter is the capitalized name with the set prefix (setModel). This is much easier than manually creating accessors for every property. The _model portion of the synthesize statement defines the private instance variable name to use for the property.

As of Xcode 4.4, properties declared with @property will be automatically synthesized, so it’s safe to omit the @synthesize line if you’re ok with the default instance variable naming conventions.

The drive implementation has the same signature as the interface, but it’s followed by whatever code should be executed when the method is called. Note how we accessed the value via self.model instead of the _model instance variable. This is a best practice step because it utilizes the property’s accessor methods. Typically, the only place you’ll need to directly access instance variables is in init methods and the deallocmethod.

The self keyword refers to the instance calling the method (like this in C++ and Java). In addition to accessing properties, this can be used to call other methods defined on the same class (e.g.,[self anotherMethod]). We’ll see many examples of this throughout the tutorial.

Instantiation and Usage

Any files that need access to a class must import its header file (Car.h)—they should never, ever try to access the implementation file directly. That would defeat the goal of separating the public API from its underlying implementation. So, to see our Car class in action, change main.m to the following.

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *toyota = [[Car alloc] init];
        
        [toyota setModel:@"Toyota Corolla"];
        NSLog(@"Created a %@", [toyota model]);
        
        toyota.model = @"Toyota Camry";
        NSLog(@"Changed the car to a %@", toyota.model);
        
        [toyota drive];
        
    }
    return 0;
}

After the interface has been imported with the #import directive, you can instantiate objects with the alloc/init pattern shown above. As you can see, instantiation is a two-step process: first you must allocate some memory for the object by calling the alloc method, then you need to initialize it so it’s ready to use. You should never use an uninitialized object.

It’s worth repeating that all objects must be stored as pointers. This is why we used Car *toyota instead of Car toyota to declare the variable.

To call a method on an Objective-C object, you place the instance and the method in square brackets, separated by a space. Arguments are passed after the method name, preceded by a colon. So, if you’re coming from a C++, Java, or Python background, the [toyota setModel:@"Toyota Corolla"] call would translate to:

toyota.setModel("Toyota Corolla");

This square-bracket syntax can be unsettling for newcomers to the language, but rest assured, you’ll be more than comfortable with Objective-C’s method conventions after reading through the Methodsmodule.

This example also shows you both ways to work with an object’s properties. You can either use the synthesized model and setModelaccessor methods, or you can use the convenient dot-syntax, which should be more familiar to developers who have been using Simula-style languages.

Class Methods and Variables

The above snippets define instance-level properties and methods, but it’s also possible to define class-level ones. These are commonly called “static” methods/properties in other programming languages (not to be confused with the static keyword).

Class method declarations look just like instance methods, except they are prefixed with a plus sign instead of a minus sign. For example, let’s add the following class-level method to Car.h:

// Car.h
+ (void)setDefaultModel:(NSString *)aModel;

Similarly, a class method implementation is also preceded by a plus sign. While there is technically no such thing as a class-level variable in Objective-C, you can emulate one by declaring a static variable before defining the implementation:

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

static NSString *_defaultModel;

@implementation Car {
...

+ (void)setDefaultModel:(NSString *)aModel {
    _defaultModel = [aModel copy];
}

@end

The [aModel copy] call creates a copy of the parameter instead of assigning it directly. This is what’s going on under the hood when we used the (copy) attribute on the model property.

Class methods use the same square-bracket syntax as instance methods, but they must be called directly on the class, as shown below. They cannot be called on an instance of that class ([toyota setDefaultModel:@"Model T"] will throw an error).

// main.m
[Car setDefaultModel:@"Nissan Versa"];

“Constructor” Methods

There are no constructor methods in Objective-C. Instead, an object isinitialized by calling the init method immediately after it’s allocated. This is why instantiation is always a two-step process: allocate, then initialize. There is also a class-level initialization method that will be discussed in a moment.

init is the default initialization method, but you can also define your own versions to accept configuration parameters. There’s nothing special about custom initialization methods—they’re just normal instance methods, except the method name should always begin withinit. An exemplary “constructor” method is shown below.

// Car.h
- (id)initWithModel:(NSString *)aModel;

To implement this method, you should follow the canonical initialization pattern shown in initWithModel: below. The super keyword refers to the parent class, and again, the self keyword refers to the instance calling the method. Go ahead and add the following methods to Car.m.

// Car.m
- (id)initWithModel:(NSString *)aModel {
    self = [super init];
    if (self) {
        // Any custom setup work goes here
        _model = [aModel copy];
        _odometer = 0;
    }
    return self;
}

- (id)init {
    // Forward to the "designated" initialization method
    return [self initWithModel:_defaultModel];
}

Initialization methods should always return a reference to the object itself, and if it cannot be initialized, it should return nil. This is why we need to check if self exists before trying to use it. There should typically only be one initialization method that needs to do this, and the rest should forward calls to this designated initializer. This eliminates boilerplate code when you have several custom init methods.

Also notice how we directly assigned values to the _model and_odometer instance variables in initWithModel:. Remember that this is one of the only places you should do this—in the rest of your methods you should be using self.model and self.odometer.

Class-Level Initialization

The initialize method is the class-level equivalent of init. It gives you a chance to set up the class before anyone uses it. For example, we can use this to populate the _defaultModel variable with a valid value, like so:

// Car.m
+ (void)initialize {
    if (self == [Car class]) {
        // Makes sure this isn't executed more than once
        _defaultModel = @"Nissan Versa";
    }
}

The initialize class method is called once for every class before the class is used. This includes all subclasses of Car, which means that Carwill get two initialize calls if one of its subclasses didn’t re-implement it. As a result, it’s good practice to use the self == [Car class] conditional to ensure that the initialization code is only run once. Also note that in class methods, the self keyword refers to the class itself, not an instance.

Objective-C doesn’t force you to mark methods as overrides. Even though init and initialize are both defined by its superclass, NSObject, the compiler won’t complain when you redefine them in Car.m.

The next iteration of main.m shows our custom initialization methods in action. Before the first time the class is used, [Car initialize] is called automatically, setting _defaultModel to @"Nissan Versa". This can be seen in the first NSLog(). You can also see the result of the custom initialization method (initWithModel:) in the second log output.

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        // Instantiating objects
        Car *nissan = [[Car alloc] init];
        NSLog(@"Created a %@", [nissan model]);
        
        Car *chevy = [[Car alloc] initWithModel:@"Chevy Corvette"];
        NSLog(@"Created a %@, too.", chevy.model);
        
    }
    return 0;
}

Dynamic Typing

Classes themselves are represented as objects, which makes it possible to query their properties (introspection), or even change their behavior on-the-fly (reflection). These are very powerful dynamic typing capabilities, as they let you call methods and set properties on objects even when you don’t know what type of object they are.

The easiest way to get a class object is via the class class-level method (apologies for the redundant terminology). For example, [Car class]returns an object representing the Car class. You can pass this object around to methods like isMemberOfClass: and isKindOfClass: to get information about other instances. A comprehensive example is included below.

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *delorean = [[Car alloc] initWithModel:@"DeLorean"];
        
        // Get the class of an object
        NSLog(@"%@ is an instance of the %@ class",
              [delorean model], [delorean class]);
        
        // Check an object against a class and all subclasses
        if ([delorean isKindOfClass:[NSObject class]]) {
            NSLog(@"%@ is an instance of NSObject or one "
                  "of its subclasses",
                  [delorean model]);
        } else {
            NSLog(@"%@ is not an instance of NSObject or "
                  "one of its subclasses",
                  [delorean model]);
        }
        
        // Check an object against a class, but not its subclasses
        if ([delorean isMemberOfClass:[NSObject class]]) {
            NSLog(@"%@ is a instance of NSObject",
                  [delorean model]);
        } else {
            NSLog(@"%@ is not an instance of NSObject",
                  [delorean model]);
        }
        
        // Convert between strings and classes
        if (NSClassFromString(@"Car") == [Car class]) {
            NSLog(@"I can convert between strings and classes!");
        }
    }
    return 0;
}

The NSClassFromString() function is an alternative way to get your hands on a class object. This is very flexible, as it lets you dynamically request class objects at runtime; however, it’s also rather inefficient. For this reason, you should opt for the class method whenever possible.

If you’re interested in dynamic typing, be sure to check out Selectorsand The id Type.

Summary

In this module, we learned how to create classes, instantiate objects, define initialization methods, and work with class-level methods and variables. We also took a brief look at dynamic typing.

The previous module mentioned that Objective-C doesn’t support namespaces, which is why the Cocoa functions require prefixes like NSCAAV, etc to avoid naming collisions. This applies to classes, too. The recommended convention is to use a three-letter prefix for your application-specific classes (e.g., XYZCar).

While this is pretty much everything you need to know to start writing your own classes, we did skim over some important details, so don’t worry if you’re feeling not entirely comfortable with properties or methods. The next module will begin filling in these holes with a closer look at the @property directive and all of the attributes that affect its behavior.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据库采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方式,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据库。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。
数字社区解决方案是一套综合性的系统,旨在通过新基建实现社区的数字化转型,打通智慧城市建设的"最后一公里"。该方案以国家政策为背景,响应了国务院、公安部和中央政法会议的号召,强调了社会治安防控体系的建设以及社区治理创新的重要性。 该方案的建设标准由中央综治办牵头,采用"9+X"模式,通过信息采集、案(事)件流转等手段,实现五级信息中心的互联互通,提升综治工作的可预见性、精确性和高效性。然而,当前社区面临信息化管理手段不足、安全隐患、人员动向难以掌握和数据资源融合难等问题。 为了解决这些问题,数字社区建设目标提出了"通-治-服"的治理理念,通过街道社区、区政府、公安部门和居民的共同努力,实现社区的平安、幸福和便捷。建设思路围绕"3+N"模式,即人工智能、物联网和数据资源,结合态势感知、业务分析和指挥调度,构建起一个全面的数据支持系统。 数字社区的治理体系通过"一张图"实现社区内各维度的综合态势可视化,"一套表"进行业务分析,"一张网"完成指挥调度。这些工具共同提升了社区治理的智能化和效率。同时,数字社区还提供了包括智慧通行、智慧环保、居家养老和便民服务等在内的多样化数字服务,旨在提升居民的生活质量。 在硬件方面,数字社区拥有IOT物联网边缘网关盒子和AI边缘分析盒子,这些设备能够快速集成老旧小区的物联设备,实现传统摄像设备的智能化改造。平台优势体现在数字化能力中台和多样化的应用,支持云、边、端的协同工作,实现模块化集成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值