Cùng với biến, câu lệch điều kiện, và vòng lặp, thì hàm (functions) là 1 trong những thành phần cơ bản của bất cứ mọi ngôn ngữ lập trình. Chúng giúp bạn có thể sử dụng lại những đoạn code xuyên suốt trong chương trình, thứ mà rất cần thiết cho việc kiến trúc và bảo trì. Bạn sẽ tìm thấy nhiều ví dụ của hàm ở iOS, OS X frameworks.

Giống như các kiến trúc đơn giản khác, Objective – C hoàn toàn dựa trên ngôn ngữ lập trình C về functions. Mô đun này giới thiệu phần diện mạo quan trong nhất của C functions, bao gồm cú pháp đơn giản, sự phân chia của phần khai báo và phần thực thi, các vấn đề thường gặp về phạm vi trong hàm, và những cân nhắc (phân chia ) thư viện chức năng.

Basic Syntax

Có 4 thành phần cấu tạo lên 1 hàm trong C :[ giá trị trả vê ] [tên hàm ] [tham số truyền  vào ] và [ đoạn code liên quan (nội dung của hàm )]. Sau khi bạn đã  định nghĩa được chúng, bạn có thể gọi hàm để thực thi đoạn code bên trong hàm (nội dung của hàm ) bằng cách truyền vào những tham số cần thiết giữa cặp ngoặc tròn.

Ví dụ, đoạn code sau đây định nghĩa 1 hàm gọi là getRandomInteger(), chấp nhận 2 tham số truyền vào là 2 giá trị Integer, và kết quả trả về là 1 giá trị Integer khác. Bên trong hàm, chúng ta truy xuất 2 tham số truyền vào bằng 2 biến minimum và maximum,  sau đó giá trị trả về là 1 giá trị đã được tính toán thông qua từ khoá return. Trong hàm main(), chúng ta gọi hàm getRandomInteger và tham số truyền vào là -10 và 10.

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

int getRandomInteger(int minimum, int maximum) {
    return arc4random_uniform((maximum - minimum) + 1) + minimum;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int randomNumber = getRandomInteger(-10, 10);
        NSLog(@"Selected a random number between -10 and 10: %d",
              randomNumber);
    }
    return 0;
}

Hàm arc4random_uniform() ( hàm này có sẵn trong frameworks) trả về 1 giá trị bất kì trong khoảng giữa 0 và giá trị mà bạn truyền vào. (Nó được ưa thích hơn 2 thuật toán cũ là rand() và random() ).

Functions giúp bạn sử dụng con trỏ tham chiếu là giá trị trả về hoặc tham số, điều đó có nghãi là chúng có thể tích hợp 1 cách liền mạc với Objective – C object, (luôn nhớ rằng tất cả các object được thể hiện bằng con trỏ ).  Ví dụ, thử thay đổi file main.m như sau.

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

NSString *getRandomMake(NSArray *makes) {
    int maximum = (int)[makes count];
    int randomIndex = arc4random_uniform(maximum);
    return makes[randomIndex];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *makes = @[@"Honda", @"Ford", @"Nissan", @"Porsche"];
        NSLog(@"Selected a %@", getRandomMake(makes));
    }
    return 0;
}

Hàm getRandomMake() chấp nhận tham số truyền vào là 1 đối tượng kiểu NSArray và giá trị trả về là 1 đối tượng kiểu NSString. Chú ý: nó sử dụng cú pháp tương tự như dấu sao khai báo biến con trỏ.

Declarations vs Implementations

Các hàm cần được định nghĩa trước khi chúng được sử dụng. Nếu như bạn định nghĩa hàm getRandomMake() (trong ví dụ trên ) sau hàm main(),  trình biên dịch sẽ không thể tìm thấy nó khi mà bạn cố gắng để gọi nó trong hàm main(). Nó áp đặt một cấu trúc khá chặt chẽ về phát triền và có thể làm cho nó khó khăn để tổ chức, kiến trúc những ứng dụng lớn hơn. Để khắc phục nhược điểm này, ngôn ngữ C cho phép bạn phân chia phần khai báo của 1 hàm với phần thực thi của nó.

Học lập trình iOS cơ bản đến nâng cao

Phần khai báo của 1 hàm nói cho trình biên dịch biết được tham số truyền vào, giá trị trả về sẽ như thế nào. Bằng việc cung cấp kiểu dữ liệu cho giá trị trả về và các tham số truyền vào của 1 hàm, trình biên dịch có thể chắc chắn rằng bạn đang sử dụng nó chính xác mà không cần biết thực chất nó làm gì.  Phần thực thi tương sẽ gắn đoạn code thực thi với phần khai báo của hàm. Kết hợp với nhau, chúng sẽ cho bạn 1 khai báo hàm hoàn chỉnh.

Ví dụ dưới đây khai báo hàm getRandomMake() mà nó có thể sử dụng trong main() trước khi nó được thực hiện. Chú ý: phần khai báo chỉ cần kiểu dữ liệu của tham số chuyền vào,  tên tham số có thể bị bỏ qua.
Như chúng ta sẽ thấy trong phần Thư viện hàm, phân chia phần khai báo của 1 hàm với phần thực thi rất hữu ích, hữu dụng đối với việc tổ chức, kiến trúc những framework lớn.

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

// Declaration
NSString *getRandomMake(NSArray *);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *makes = @[@"Honda", @"Ford", @"Nissan", @"Porsche"];
        NSLog(@"Selected a %@", getRandomMake(makes));
    }
    return 0;
}

// Implementation
NSString *getRandomMake(NSArray *makes) {
    int maximum = (int)[makes count];
    int randomIndex = arc4random_uniform(maximum);
    return makes[randomIndex];
}

Như chúng ta sẽ thấy trong phần Thư viện hàm, phân chia phần khai báo của 1 hàm với phần thực thi rất hữu ích, hữu dụng đối với việc tổ chức, kiến trúc những framework lớn.

The Static Keyword

Từ khoá tĩnh giúp bạn thay đổi tính sẵn dùng của 1 hàm hoặc biến. Đáng tiêcs là nó có ảnh hưởng khác nhau tuỳ theo vị trí mà bạn sử dụng nó. Trong phần này sẽ làm rõ 2 trường hợp thông thường của từ khoá tĩnh.

Static Functions

Mặc định, tất cả các hàm đều có phạm vi toàn cục. Điều này có nghĩa là ngay khi bạn định nghĩa 1 hàm trong 1 file, nó ngay lập tức có thể sự dụng ở bất kỳ chỗ nào khác. Xác định từ khoá tĩnh giúp bạn giới hạn phạm vi của hàm với file hiện thời, rất hữu dụng trong việc khởi tạo những hàm riêng biệt (không truy xuất từ bên ngoài ) và tránh xung đột khi đặt tên.

Globally-scoped functions vs. statically-scoped functions
Globally-scoped functions vs. statically-scoped functions

Ví dụ dưới đây sẽ cho bạn thấy làm thế nào để khởi tạo 1 hàm tĩnh.  Nếu bạn đã viết đoạn code vào file khác, bạn sẽ không thể truy xuất hàm getRandomInteger() trong main(). Chú ý : từ khoá tĩnh nen được sử dụng trong cả phần khai báo hàm và phần thực thi hàm.

// Static function declaration
static int getRandomInteger(int, int);

// Static function implementation
static int getRandomInteger(int minimum, int maximum) {
    return arc4random_uniform((maximum - minimum) + 1) + minimum;
}

Static Local Variables

Biến được khai báo bên trong 1 hàm được reset mỗi khi hàm được gọi. Đây là 1 hành vi mặc định trực quan, như chức năng cư xử 1 cách nhất quán bất kể  có bao nhiêu lần bạn gọi nó. Tuy nhiên, khi bạn sử dụng từ khoá tĩnh lên 1 biến cục bộ, hàm sẽ ghi nhớ giá trị của nó qua lời gọi.

ndependent automatic variables vs. shared static variables
Independent automatic variables vs. shared static variables

Ví dụ, biến currentCount trong đoạn code dưới đây không bao giờ reset, nên thay vì lưu giữ biến đếm trong 1 biến bên trong main(), chúng ta để countByTwo() làm việc đó.

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

int countByTwo() {
    static int currentCount = 0;
    currentCount += 2;
    return currentCount;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"%d", countByTwo());    // 2
        NSLog(@"%d", countByTwo());    // 4
        NSLog(@"%d", countByTwo());    // 6
    }
    return 0;
}

Nhưng không giống như hàm tĩnh ( static function ) được thảo luận ở phần trước, việc sử dụng từ khoá tĩnh không làm ảnh hưởng đến phạm vi của biến cục bộ. Điều đó nói răng, biến cục bộ vẫn chỉ có thể truy xuất được bên trong hàm của nó.

Function Libraries

Objective – C không hỗ trợ không gian tên ( namespaces ), vì vậy để tránh việc đặt trùng tên với các hàm toàn cục, những framework lớn cần sử dụng tiền tố trong các hàm – là các định danh duy nhất. Đó chính là việc tịa sao bạn thấy các hàm có sẵn của frameworks như NSMakeRange() và CGImageCreate() thay vì chỉ đơn thuần là makeRange() hay imageCreate().

Khi bạn khởi tạo thư viện hàm của mình, bạn nên định nghĩa các hàm trong 1 file header, và thực thi chúng trong các file thực thi riêng biệt.  Nó giúp việc sử dụng thư viện hàm đơn giản, chỉ cần import file header mà không cần quan tâm các hàm bên trong thư viên hàm được thực thi như thế nào. Ví dụ, header cho thư viện hàm CarUtilities có thể tương tự như dưới đây

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

NSString *CUGetRandomMake(NSArray *makes);
NSString *CUGetRandomModel(NSArray *models);
NSString *CUGetRandomMakeAndModel(NSDictionary *makesAndModels);

File thực thi tương ứng sẽ định nghĩa những gì mà hàm sẽ thực hiện. Khi mà các file khác không cần thiết phải import vào phần thực thi, bạn có thể sử dụng định nghĩa static ( tĩnh) để tạo ra các hàm riêng biệt (private ) để sử dụng nội bộ trong thư viện.

// CarUtilities.m
#import "CarUtilities.h"

// Private function declaration
static id getRandomItemFromArray(NSArray *anArray);

// Public function implementations
NSString *CUGetRandomMake(NSArray *makes) {
    return getRandomItemFromArray(makes);
}
NSString *CUGetRandomModel(NSArray *models) {
    return getRandomItemFromArray(models);
}
NSString *CUGetRandomMakeAndModel(NSDictionary *makesAndModels) {
    NSArray *makes = [makesAndModels allKeys];
    NSString *randomMake = CUGetRandomMake(makes);
    NSArray *models = makesAndModels[randomMake];
    NSString *randomModel = CUGetRandomModel(models);
    return [randomMake stringByAppendingFormat:@" %@", randomModel];
}

// Private function implementation
static id getRandomItemFromArray(NSArray *anArray) {
    int maximum = (int)[anArray count];
    int randomIndex = arc4random_uniform(maximum);
    return anArray[randomIndex];
}

Bây giờ, ở main.m đã có thể import header file và sử dụng các hàm được định nghĩa trong file header. Chú ý: việc gọi hàm static getRandomItemFromArray() từ main.m sẽ có lỗi trong trình biên dịch.

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDictionary *makesAndModels = @{
            @"Ford": @[@"Explorer", @"F-150"],
            @"Honda": @[@"Accord", @"Civic", @"Pilot"],
            @"Nissan": @[@"370Z", @"Altima", @"Versa"],
            @"Porsche": @[@"911 Turbo", @"Boxster", @"Cayman S"]
        };
        NSString *randomCar = CUGetRandomMakeAndModel(makesAndModels);
        NSLog(@"Selected a %@", randomCar);
    }
    return 0;
}

Summary

Phần này đã giới thiệu xong về ngôn ngữ lập trình C dưới cái nhìn về hàm (functions ). Chúng ta đã học được cách làm thế nào để khai báo và thực thi hàm, thay đổi phạm vi của hàm, làm chúng ghi nhớ lại các biến cục bộ và tổ chức, kiến trúc những thư viện hàm lớn.

Trong khi phần lớn chức năng đằng sau Cocoa và Cocoa Touch frameworks được đóng gói dưới dạng Objective – C class, không có sự thiếu sót nào của các hàm được xây dựng sẵn.  Có một số hàm bạn sẽ rất hay gặp phải là các hàm tiện ích như NSLog(), NSMakeRect() để khởi tạo, cấu hình nhưng đỗi tượng phức tạp, sử dụng các API thân thiện

Bây giờ chúng ta đã sẵn sàng bắt đầu giải quyết các khía cạnh hướng đối tượng của Objective – C. Trong phần kế tiếp, chúng ta sẽ học cách làm sao để định nghĩa các lớp, khởi tạo đối tượng, gán giá trị cho các thuộc tính, và sử dụng các phương thức.

Bài dịch từ Ry’s tutorial

Khóa học lập trình di động tại Techmaster:

Để cài đặt MacOSX lên phần cứng không phải Apple liên hệ chuyên gia cài Hackintosh:

  • Nguyễn Minh Sơn: 01287065634
  • Huỳnh Minh Sơn: 0936225565
  • Website: caidatmacos.com