前言:博主為一枚因?yàn)楣ぷ餍枰谄D難地新學(xué) IOS 開發(fā)的程序媛淡喜,對(duì)于 Objective-C 還不甚了解陨帆,所以博文內(nèi)容可能不是那么嚴(yán)謹(jǐn),如有童鞋發(fā)現(xiàn)不妥之處已亥,還望告知博主熊赖,萬分感謝!
問題描述
博主今早在看一份 Objective-C 的代碼時(shí)虑椎,在 SignInViewController.m 文件中看到如下代碼片段:
#import "SignInViewController.h"
@interface SignInViewController ()
@property (weak, nonatomic) IBOutlet UITextField *userNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *userPassTextField;
@property (weak, nonatomic) IBOutlet UIButton *signInBtn;
@property (strong, nonatomic) NSString *userName;
@property (strong, nonatomic) NSString *userPass;
@end
然后博主在仿照寫代碼時(shí)震鹉,一不留神將 @interface SignInViewController () 寫成了 @interface SignInViewController : NSOject,然后 XCode 給出了錯(cuò)誤提示捆姜,后面回查才發(fā)現(xiàn)自己把代碼寫錯(cuò)了传趾,并且還并不了解 .m 文件中的這個(gè) @interface SignInViewController () 是什么意思,所以就查了一波資料娇未。
問題解析
查閱資料后發(fā)現(xiàn)墨缘,該處涉及到 Objective-C 中分類和擴(kuò)展的知識(shí)點(diǎn)星虹。
知識(shí)點(diǎn)分析
分類 Category
官方解釋:
A category allows you to add methods to an existing class
—even to one for which you do not have the source.
Categories are a powerful feature that allows you to
extend the functionality of existing classes without subclassing.
Using categories, you can also distribute the implementation
of your own classes among several files.
Adding Methods to Classes
You can add methods to a class by declaring them in an interface file
under a category name and defining them in an implementation file
under the same name.
The category name indicates that the methods are additions to a class declared elsewhere,
not a new class.
You cannot, however, use a category to add additional instance variables to a class.
The methods the category adds become part of the class type.
Category methods can do anything that methods defined in the class proper can do.
At runtime, there’s no difference.
The methods the category adds to the class are inherited by all the class’s subclasses,
just like other methods.
The declaration of a category interface looks very much like a class interface declaration—
except the category name is listed within parentheses after the class name
and the superclass isn’t mentioned.
Unless its methods don’t access any instance variables of the class,
the category must import the interface file for the class it extends:
#import "ClassName.h"
@interface ClassName ( CategoryName )
// method declarations
@end
Note that a category can’t declare additional instance variables for the class;
it includes only methods.
However, all instance variables within the scope of the class
are also within the scope of the category.
That includes all instance variables declared by the class,
even ones declared @private.
There’s no limit to the number of categories that you can add to a class,
but each category name must be different,
and each should declare and define a different set of methods.
分類允許開發(fā)者為一個(gè)已有的類添加新功能零抬,即便開發(fā)者沒有該類的源碼也可以如此使用镊讼。分類給予了開發(fā)者一個(gè)擴(kuò)展已有類功能,而無需繼承該類的強(qiáng)大特性平夜,提供了不同于繼承的便利蝶棋。通過分類為類添加的方法就自然而然成為該類的一部分,類似于該類原來已有的方法忽妒,所有繼承自該類的子類均自動(dòng)擁有這些方法玩裙。
注意:
- 可以通過分類為已有類添加新功能,而不能通過分類添加新的實(shí)例變量段直;
- 除非在分類中實(shí)現(xiàn)的方法均無需訪問該類中的實(shí)例變量吃溅,否則在分類中必須導(dǎo)入類的 interface 文件;
- 導(dǎo)入了類的 interface 文件后鸯檬,該類中定義的所有實(shí)例變量均自動(dòng)被分類所擁有决侈,即便是 @private 定義的實(shí)例變量,分類也可以訪問喧务;
- 可以為一個(gè)類添加任意數(shù)量的分類赖歌,只需要保證每個(gè)分類名稱及定義在分類中的方法不同即可。
分類的使用:
使用步驟:
第一步:創(chuàng)建一個(gè)在指定分類名下帶有接口的新文件功茴,在該文件中添加想要添加的新功能庐冯;
第二步:在同名 implementation 文件中實(shí)現(xiàn)這些功能
#import <Foundation/Foundation.h>
/** NSString 表示將要添加分類的類名稱,該類必須是已存在的坎穿。
* Test 為分類名稱
* testString 為新添加的方法
*/
@interface NSString (Test)
-(NSString*) testString;
@end
@implementation NSString (Test)
-(NSString*) testString
{
// 方法實(shí)現(xiàn)
NSString str = @"test string";
return str;
}
@end
擴(kuò)展 Extension
官方解釋:
Class extensions are like anonymous categories,
except that the methods they declare must be implemented
in the main @implementation block for the corresponding class.
Using the Clang/LLVM 2.0 compiler, you can also declare properties
and instance variables in a class extension.
A common use for class extensions is to redeclare property that
is publicly declared as read-only privately as readwrite:
@interface MyClass : NSObject
@property (retain, readonly) float value;
@end
// Private extension, typically hidden in the main implementation file.
@interface MyClass ()
@property (retain, readwrite) float value;
@end
Notice that (in contrast to a category) no name is given in the parentheses
in the second @interface block.
It is also generally common for a class to have a publicly declared API and
to then have additional methods declared privately for use solely by the class
or the framework within which the class resides. Class extensions allow you
to declare additional required methods for a class in locations other than
within the primary class @interface block.
大意為:擴(kuò)展和匿名分類很像展父,不過和匿名分類還是有很大的區(qū)別,擴(kuò)展中可以添加實(shí)例變量玲昧,擴(kuò)展中定義的方法必須在相應(yīng)類的 @implementation 代碼塊中實(shí)現(xiàn)犯祠。從 Xcode 4 之后就推薦在自定義類的 .m 文件中使用擴(kuò)展,這樣就能保證良好的代碼封裝性酌呆,避免把私有接口暴露給外面衡载。
擴(kuò)展的慣用法:
- 重新將一個(gè)在外部 interface 中聲明為 readonly 的變量聲明為 readwrite 類型,既便于在當(dāng)前類中讀寫該變量隙袁,又能夠避免外部修改痰娱;
- 在外部 interface 中聲明公有 API,而在擴(kuò)展中聲明私有方法菩收,可以避免將私有接口暴露于外部梨睁。
擴(kuò)展的使用步驟:
第一步:創(chuàng)建 MyClass.h 文件,在 @interface MyClass 中聲明可供外部調(diào)用的公有 API娜饵;
第二步:創(chuàng)建 MyClass.m 文件坡贺,在 @interface MyClass(){} 擴(kuò)展中聲明所需的實(shí)例變量和方法;
第三步:在 @implementation 代碼塊中實(shí)現(xiàn)在 MyClass.h 文件和 @interface MyClass(){} 擴(kuò)展中聲明的方法。
// 如下代碼位于 MyClass.h 文件中
@interface MyClass : NSObject
- (float)value;
@end
// 如下代碼位于 MyClass.m 文件中
@interface MyClass () { // 擴(kuò)展
float value;
}
- (void)setValue:(float)newValue;
@end
// 實(shí)現(xiàn)
@implementation MyClass
- (float)value {
return value;
}
- (void)setValue:(float)newValue {
value = newValue;
}
@end
分類與擴(kuò)展的區(qū)別
- 分類只能為已有類添加新功能遍坟,不能在分類中添加新的實(shí)例變量拳亿;而擴(kuò)展不僅可以添加新功能,還能添加實(shí)例變量肺魁;
- 通過分類為已有類添加新功能時(shí)隔节,需要在類名后面的小括號(hào)中指定分類名,同時(shí)可以通過指定多個(gè)分類名為已有類添加這多個(gè)分類中的功能瘾晃;而使用擴(kuò)展時(shí)幻妓,類名后的小括號(hào)中不添加任何東西涌哲;
- 擴(kuò)展中聲明的方法必須在相應(yīng)類的 @implementation 代碼塊中實(shí)現(xiàn),而分類則沒有此要求哪廓,也就是說分類與類之間是獨(dú)立的,不與特定的類相關(guān)涡真,而擴(kuò)展是與特定的類相關(guān)的哆料,所以在擴(kuò)展中聲明的代碼必須在相關(guān)類的 @implementation 主代碼塊中實(shí)現(xiàn)