Objective-C中編程中一些推薦的書(shū)寫(xiě)規(guī)范小結(jié)
一、類(lèi)
1. 類(lèi)名
類(lèi)名應(yīng)該以三個(gè)大寫(xiě)字母作為前綴(雙字母前綴為Apple的類(lèi)預(yù)留)
不僅僅是類(lèi),公開(kāi)的常量、Protocol等的前綴都為相同的三個(gè)大寫(xiě)字母。
當(dāng)你創(chuàng)建一個(gè)子類(lèi)的時(shí)候,你應(yīng)該把說(shuō)明性的部分放在前綴和父類(lèi)名的中間。
例如:
如果你有一個(gè) ZOCNetworkClient 類(lèi),子類(lèi)的名字會(huì)是ZOCTwitterNetworkClient (注意 "Twitter" 在 "ZOC" 和 "NetworkClient" 之間); 按照這個(gè)約定, 一個(gè)UIViewController 的子類(lèi)會(huì)是 ZOCTimelineViewController.
2. Initializer和dealloc
推薦的代碼組織方式是將dealloc方法放在實(shí)現(xiàn)文件的最前面(直接在@synthesize以及@dynamic之后),init應(yīng)該跟在dealloc方法后面。
如果有多個(gè)初始化方法,那么指定初始化方法應(yīng)該放在最前面,間接初始化方法跟在后面。
如今有了ARC,dealloc方法幾乎不需要實(shí)現(xiàn),不過(guò)把init和dealloc放在一起,強(qiáng)調(diào)它們是一對(duì)的。通常在init方法中做的事情需要在dealloc方法中撤銷(xiāo)。
關(guān)于指定初始化方法(designated initializer)和間接初始化方法(secondary initializer)
Objective-C 有指定初始化方法(designated initializer)和間接(secondary initializer)初始化方法的觀念。 designated 初始化方法是提供所有的參數(shù),secondary 初始化方法是一個(gè)或多個(gè),并且提供一個(gè)或者更多的默認(rèn)參數(shù)來(lái)調(diào)用 designated 初始化的初始化方法。
@implementation ZOCEvent
- (instancetype)initWithTitle:(NSString *)title
date:(NSDate *)date
location:(CLLocation *)location
{
self = [super init];
if (self) {
_title = title;
_date = date;
_location = location;
}
return self;
}
- (instancetype)initWithTitle:(NSString *)title
date:(NSDate *)date
{
return [self initWithTitle:title date:date location:nil];
}
- (instancetype)initWithTitle:(NSString *)title
{
return [self initWithTitle:title date:[NSDate date] location:nil];
}
@end
initWithTitle:date:location: 就是 designated 初始化方法,另外的兩個(gè)是 secondary 初始化方法。因?yàn)樗鼈儍H僅是調(diào)用類(lèi)實(shí)現(xiàn)的 designated 初始化方法。
一個(gè)類(lèi)應(yīng)該有且只有一個(gè) designated 初始化方法,其他的初始化方法應(yīng)該調(diào)用這個(gè) designated 的初始化方法(有例外)。
3. 當(dāng)定義一個(gè)新類(lèi)的時(shí)候有三個(gè)不同的方式:
(1)不需要重載任何初始化函數(shù)
(2)重載 designated initializer
(3)定義一個(gè)新的 designated initializer
第一種方式不需要增加類(lèi)的任何初始化邏輯,也就是說(shuō)在類(lèi)中不必重寫(xiě)父類(lèi)的初始化方法也不需要其他操作。
第二種方式要重載父類(lèi)的指定初始化方法。例子:
@implementation ZOCViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
// call to the superclass designated initializer
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization (自定義的初始化過(guò)程)
}
return self;
}
@end
這個(gè)例子中,ZOCViewController繼承自UIViewController,這里我們有一些其他的需求(比如希望在初始化的時(shí)候給一些成員變量賦值),所以需要重寫(xiě)父類(lèi)的指定初始化方法initWithNibName:bundle:方法。
注意,如果在這里并沒(méi)有重載這個(gè)方法,而是重載了父類(lèi)的init方法,那么會(huì)是一個(gè)錯(cuò)誤。
因?yàn)樵趧?chuàng)建這個(gè)類(lèi)(ZOCViewController)的時(shí)候,會(huì)調(diào)用initWithNib:bundle:這個(gè)方法,所以我們重載這個(gè)方法,首先保證父類(lèi)初始化成功,然后在這個(gè)方法中進(jìn)行額外的初始化操作。但是如果重載init方法,在創(chuàng)建這個(gè)類(lèi)的時(shí)候,并不會(huì)調(diào)用init方法(調(diào)用的是initWithNib:bundle:這個(gè)指定初始化方法)。
第三種方式是希望提供自己的類(lèi)初始化方法,應(yīng)該遵守下面三個(gè)步驟來(lái)保證正確性:
定義你的 designated initializer,確保調(diào)用了直接超類(lèi)的 designated initializer。
重載直接超類(lèi)的 designated initializer。調(diào)用你的新的 designated initializer。
為新的 designated initializer 寫(xiě)文檔。
很多開(kāi)發(fā)者會(huì)忽略后兩步,這不僅僅是一個(gè)粗心的問(wèn)題,而且這樣違反了框架的規(guī)則,而且可能導(dǎo)致不確定的行為和bug。
正確的例子:
@implementation ZOCNewsViewController
- (id)initWithNews:(ZOCNews *)news
{
// call to the immediate superclass's designated initializer (調(diào)用直接超類(lèi)的 designated initializer)
self = [super initWithNibName:nil bundle:nil];
if (self) {
_news = news;
}
return self;
}
// Override the immediate superclass's designated initializer (重載直接父類(lèi)的 designated initializer)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
// call the new designated initializer
return [self initWithNews:nil];
}
@end
很多開(kāi)發(fā)者只會(huì)寫(xiě)第一個(gè)自定義的初始化方法,而不重載父類(lèi)的指定初始化方法。
在第一個(gè)自定義的初始化方法中,因?yàn)槲覀円x自己的指定初始化方法,所以在最開(kāi)始的時(shí)候首先要調(diào)用父類(lèi)的指定初始化方法以保證父類(lèi)都初始化成功,這樣ZOCNewsViewController才是可用狀態(tài)。(因?yàn)楦割?lèi)是通過(guò)initWithNibName:bundle:這個(gè)指定初始化方法創(chuàng)建的,所以我們要調(diào)用父類(lèi)的這個(gè)方法來(lái)保證父類(lèi)初始化成功)。然后在后面給_news賦值。
如果僅僅是這樣做是存在問(wèn)題的。調(diào)用者如果調(diào)用initWithNibName:bundle:來(lái)初始化這個(gè)類(lèi)也是完全合法的,如果是這種情況,那么initWithNews:這個(gè)方法永遠(yuǎn)不會(huì)被調(diào)用,所以_news = news也不會(huì)被執(zhí)行,這樣導(dǎo)致了不正確的初始化流程。
解決方法就是需要重載父類(lèi)的指定初始化方法,在這個(gè)方法中返回新的指定初始化方法(如例子中做的那樣),這樣無(wú)論是調(diào)用哪個(gè)方法都可以成功初始化。
間接初始化方法是一種提供默認(rèn)值、行為到初始化方法的方法。
你不應(yīng)該在間接初始化方法中有初始化實(shí)例變量的操作,并且你應(yīng)該一直假設(shè)這個(gè)方法不會(huì)得到調(diào)用。我們保證的是唯一被調(diào)用的方法是 designated initializer。
這意味著你的 secondary initializer 總是應(yīng)該調(diào)用 Designated initializer 或者你自定義(上面的第三種情況:自定義Designated initializer)的 self的 designated initializer。有時(shí)候,因?yàn)殄e(cuò)誤,可能打成了 super,這樣會(huì)導(dǎo)致不符合上面提及的初始化順序。
也就是說(shuō),你可能看到一個(gè)類(lèi)有多個(gè)初始化方法,實(shí)際上是一個(gè)指定初始化方法(或多個(gè),比如UITableViewController就有好幾個(gè))+多個(gè)間接初始化方法。這些簡(jiǎn)潔初始化方法可能會(huì)根據(jù)不同的參數(shù)做不同的操作,但是本質(zhì)上都是調(diào)用指定初始化方法。所以說(shuō),間接初始化方法是有可能沒(méi)有調(diào)用到的,但是指定初始化方法是會(huì)調(diào)用到的(并不是每一個(gè)都會(huì)調(diào)用到,但是最后調(diào)用的一定是一個(gè)指定初始化方法)。(這里又可以引申到上面提到的問(wèn)題,我們可以直接重寫(xiě)父類(lèi)的指定初始化方法,也可以自定義初始化方法(在這個(gè)方法中需要用到self = [super 父類(lèi)初始化方法]這種形式的代碼),并且如果是自定義初始化方法,還應(yīng)該重寫(xiě)從父類(lèi)繼承的初始化方法來(lái)返回我們的自定義初始化方法…)。
總之就是,如果重寫(xiě)父類(lèi)的指定初始化方法首先需要調(diào)用父類(lèi)的相應(yīng)初始化方法;如果增加自定義指定初始化方法,首先在新增的自定義指定初始化方法中調(diào)用父類(lèi)的相應(yīng)初始化方法,然后需要重寫(xiě)父類(lèi)的指定初始化方法,在重寫(xiě)的方法中調(diào)用剛剛添加的自定義指定初始化方法。
4.補(bǔ)充
一個(gè)類(lèi)可能有多個(gè)指定初始化方法,也有可能只有一個(gè)指定初始化方法。
以UITableViewController為例,我們可以看到:
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
它有三個(gè)指定初始化方法,我們剛才說(shuō),當(dāng)子類(lèi)從父類(lèi)繼承并重寫(xiě)初始化方法,首先需要調(diào)用父類(lèi)的初始化方法,但是如果一個(gè)類(lèi)的初始化方法有多個(gè),那么需要調(diào)用哪個(gè)呢?
事實(shí)上不同的創(chuàng)建方式要調(diào)用不同的指定初始化方法。
比如,我們以Nib的形式創(chuàng)建UITableViewController,那么最后調(diào)用的就是- (instancetype)initWithNibName:(NSString )nibNameOrNil bundle:(NSBundle )nibBundleOrNil這個(gè)指定初始化方法;如果我們以Storyboard的形式創(chuàng)建,那么最后調(diào)用的就是- (instancetype)initWithCoder:(NSCoder *)aDecoder這個(gè)指定初始化方法。如果以代碼的形式創(chuàng)建,那么最后調(diào)用的就是- (instancetype)initWithStyle:(UITableViewStyle)style這個(gè)指定初始化方法。所以不同的情況需要重寫(xiě)不同的指定初始化方法,并且重寫(xiě)的時(shí)候首先要調(diào)用父類(lèi)相應(yīng)的指定初始化方法(比如重寫(xiě)initWithCoder:方法,那么首先self = [super initWithCoder:…],都是一一對(duì)應(yīng)的)。
再以UIViewController為例,我們以Nib的形式創(chuàng)建UIViewController,那么最后調(diào)用的是- (instancetype)initWithNibName:(NSString )nibNameOrNil bundle:(NSBundle )nibBundleOrNil,這與UITableViewController是一樣的;如果我們以Storyboard的形式創(chuàng)建,那么最后調(diào)用的是- (instancetype)initWithCoder:(NSCoder )aDecoder,這與UITableViewController也是一樣的;但是如果我們以代碼的形式創(chuàng)建UIViewController(eg: CYLViewController vc = [[CYLViewController alloc] init]; CYLViewController繼承自UIViewController),那么它最后調(diào)用的實(shí)際是- (instancetype)initWithNibName:(NSString )nibNameOrNil bundle:(NSBundle )nibBundleOrNil,這與UITableViewController是不一樣的,因?yàn)閁IViewController并沒(méi)有- (instancetype)initWithStyle:(UITableViewStyle)style這個(gè)方法,所以當(dāng)用代碼創(chuàng)建的時(shí)候,最后調(diào)用的也是initWithNibName:bundle這個(gè)指定初始化方法,并且參數(shù)自動(dòng)設(shè)置為nil。
所以現(xiàn)在反過(guò)頭來(lái)再看UITableViewController,當(dāng)使用代碼的方式創(chuàng)建的時(shí)候(eg: CYLTableViewController tvc = [[CYLTableViewController alloc] init]; 或者 CYLTableViewController tvc = [[CYLTableViewController alloc] initWithStyle: UITableViewStylePlain]; ),它會(huì)調(diào)用initWithStyle:這個(gè)方法,但是如果你也實(shí)現(xiàn)了initWithNibName:bundle:這個(gè)方法,你會(huì)發(fā)現(xiàn)這個(gè)方法也被調(diào)用了。因?yàn)閁ITableViewController繼承自UIViewController,所以當(dāng)用代碼創(chuàng)建的時(shí)候,最后也會(huì)掉用到initWithNIbName:bundle:(因?yàn)閁IViewController就是這么干的)。
所以用代碼創(chuàng)建UITableViewController的時(shí)候,它會(huì)調(diào)用initWithNibName:bundle:和initWithStyle:這兩個(gè)方法。
二、屬性
屬性要盡可能描述性地命名,并且使用駝峰命名。
關(guān)于”*”的位置:
// 推薦
NSString *text;
// 不推薦
NSString* text;
NSString * text;
注意,這個(gè)習(xí)慣和常量并不同。
static NSString * const ...
你永遠(yuǎn)不能在 init (以及其他初始化函數(shù))里面用 getter 和 setter 方法,你應(yīng)該直接訪問(wèn)實(shí)例變量。記住一個(gè)對(duì)象是僅僅在 init 返回的時(shí)候,才會(huì)被認(rèn)為是初始化完成到一個(gè)狀態(tài)了。
當(dāng)使用 setter/getter 方法的時(shí)候盡量使用點(diǎn)符號(hào)。
// 推薦
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
// 不推薦
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
使用點(diǎn)符號(hào)會(huì)讓表達(dá)更加清晰并且?guī)椭鷧^(qū)分屬性訪問(wèn)和方法調(diào)用。
屬性定義
@property (nonatomic, readwrite, copy) NSString *name;
屬性的參數(shù)應(yīng)該按照這個(gè)順序排列: 原子性,讀寫(xiě)和內(nèi)存管理。
習(xí)慣上修改某個(gè)屬性的修飾符時(shí),一般從屬性名從右向左搜索需要修動(dòng)的修飾符。最可能從最右邊開(kāi)始修改這些屬性的修飾符,根據(jù)經(jīng)驗(yàn)這些修飾符被修改的可能性從高到底應(yīng)為:內(nèi)存管理 > 讀寫(xiě)權(quán)限 >原子操作
你必須使用 nonatomic,除非特別需要的情況。在iOS中,atomic帶來(lái)的鎖特別影響性能。
如果想要一個(gè)公開(kāi)的getter和私有的setter,你應(yīng)該聲明公開(kāi)的屬性為 readonly 并且在類(lèi)擴(kuò)展總重新定義通用的屬性為 readwrite 的。
//.h文件中
@interface MyClass : NSObject
@property (nonatomic, readonly, strong) NSObject *object;
@end
//.m文件中
@interface MyClass ()
@property (nonatomic, readwrite, strong) NSObject *object;
@end
@implementation MyClass
//Do Something cool
@end
描述BOOL屬性的詞如果是形容詞,那么setter不應(yīng)該帶is前綴,但它對(duì)應(yīng)的 getter 訪問(wèn)器應(yīng)該帶上這個(gè)前綴。
@property (assign, getter=isEditable) BOOL editable;
任何可以用來(lái)用一個(gè)可變的對(duì)象設(shè)置的((比如 NSString,NSArray,NSURLRequest))屬性的的內(nèi)存管理類(lèi)型必須是 copy 的。(原文中是這樣說(shuō)的,但是我理解的話(huà)并不是絕對(duì)的。如果不想讓原來(lái)的可變對(duì)象影響到類(lèi)的這個(gè)相應(yīng)屬性,那么就需要用copy,這樣在賦值的時(shí)候可變對(duì)象會(huì)首先進(jìn)行copy完成深拷貝,再把拷貝出的值賦給類(lèi)的屬性,這樣就能保證類(lèi)屬性和原來(lái)的可變對(duì)象影響并不影響。但是如果想讓類(lèi)屬性對(duì)原來(lái)的可變對(duì)象是一個(gè)強(qiáng)引用,指向這個(gè)可變對(duì)象,那么會(huì)用strong。)
你應(yīng)該同時(shí)避免暴露在公開(kāi)的接口中可變的對(duì)象,因?yàn)檫@允許你的類(lèi)的使用者改變類(lèi)自己的內(nèi)部表示并且破壞類(lèi)的封裝。你可以提供可以只讀的屬性來(lái)返回你對(duì)象的不可變的副本。
/* .h */
@property (nonatomic, readonly) NSArray *elements
/* .m */
- (NSArray *)elements {
return [self.mutableElements copy];
}
雖然使用懶加載在某些情況下很不錯(cuò),但是使用前應(yīng)當(dāng)深思熟慮,因?yàn)閼屑虞d通常會(huì)產(chǎn)生一些副作用。(但是懶加載還是比較常用的,比如下面的例子)
副作用指當(dāng)調(diào)用函數(shù)時(shí),除了返回函數(shù)值之外,還對(duì)主調(diào)用函數(shù)產(chǎn)生附加的影響。例如修改全局變量(函數(shù)外的變量)或修改參數(shù)。函數(shù)副作用會(huì)給程序設(shè)計(jì)帶來(lái)不必要的麻煩,給程序帶來(lái)十分難以查找的錯(cuò)誤,并且降低程序的可讀性。
- (NSDateFormatter *)dateFormatter {
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[_dateFormatter setLocale:enUSPOSIXLocale];
[_dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];//毫秒是SSS,而非SSSSS
}
return _dateFormatter;
}
三、方法
1.參數(shù)斷言
你的方法可能要求一些參數(shù)來(lái)滿(mǎn)足特定的條件(比如不能為nil),在這種情況下啊最好使用 NSParameterAssert() 來(lái)斷言條件是否成立或是拋出一個(gè)異常。
- (void)viewDidLoad
{
[super viewDidLoad];
[self testMethodWithAParameter:0];
}
- (void)testMethodWithAParameter: (int)value
{
NSParameterAssert(value != 0);
NSLog(@"正確執(zhí)行");
}
在此例中, 如果傳的參數(shù)為0,那么程序會(huì)拋出異常。
2.私有方法
永遠(yuǎn)不要在你的私有方法前加上 _ 前綴。這個(gè)前綴是 Apple 保留的。不要冒重載蘋(píng)果的私有方法的險(xiǎn)。
當(dāng)你要實(shí)現(xiàn)相等性的時(shí)候記住這個(gè)約定:你需要同時(shí)實(shí)現(xiàn)isEqual 和 hash方法。如果兩個(gè)對(duì)象是被isEqual認(rèn)為相等的,它們的 hash 方法需要返回一樣的值。但是如果 hash 返回一樣的值,并不能確保他們相等。
@implementation ZOCPerson
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[ZOCPerson class]]) {
return NO;
}
// check objects properties (name and birthday) for equality (檢查對(duì)象屬性(名字和生日)的相等性
...
return propertiesMatch;
}
- (NSUInteger)hash {
return [self.name hash] ^ [self.birthday hash];
}
@end
你總是應(yīng)該用 isEqualTo<#class-name-without-prefix#>: 這樣的格式實(shí)現(xiàn)一個(gè)相等性檢查方法。如果你這樣做,會(huì)優(yōu)先調(diào)用這個(gè)方法來(lái)避免上面的類(lèi)型檢查。
所以一個(gè)完整的 isEqual 方法應(yīng)該是這樣的:
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[ZOCPerson class]]) {
return NO;
}
return [self isEqualToPerson:(ZOCPerson *)object];
}
- (BOOL)isEqualToPerson:(Person *)person {
if (!person) {
return NO;
}
BOOL namesMatch = (!self.name && !person.name) ||
[self.name isEqualToString:person.name];
BOOL birthdaysMatch = (!self.birthday && !person.birthday) ||
[self.birthday isEqualToDate:person.birthday];
return haveEqualNames && haveEqualBirthdays;
}
四、Category
category 方法前加上自己的小寫(xiě)前綴以及下劃線。(真的很丑,但是蘋(píng)果也推薦這樣做)
- (id)zoc_myCategoryMethod
這是非常必要的。因?yàn)槿绻跀U(kuò)展的 category 或者其他 category 里面已經(jīng)使用了同樣的方法名,會(huì)導(dǎo)致不可預(yù)計(jì)的后果。(會(huì)調(diào)用最后一個(gè)加載的方法)
// 推薦
@interface NSDate (ZOCTimeExtensions)
- (NSString *)zoc_timeAgoShort;
@end
// 不推薦
@interface NSDate (ZOCTimeExtensions)
- (NSString *)timeAgoShort;
@end
推薦使用Category來(lái)根據(jù)不同功能對(duì)方法進(jìn)行分組。
@interface NSDate : NSObject <NSCopying, NSSecureCoding>
@property (readonly) NSTimeInterval timeIntervalSinceReferenceDate;
@end
@interface NSDate (NSDateCreation)
+ (instancetype)date;
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
+ (instancetype)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti;
+ (instancetype)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;
+ (instancetype)dateWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
// ...
@end
五、NSNotification
當(dāng)你定義你自己的 NSNotification 的時(shí)候你應(yīng)該把你的通知的名字定義為一個(gè)字符串常量,就像你暴露給其他類(lèi)的其他字符串常量一樣。你應(yīng)該在公開(kāi)的接口文件中將其聲明為 extern 的, 并且在對(duì)應(yīng)的實(shí)現(xiàn)文件里面定義。
因?yàn)槟阍陬^文件中暴露了符號(hào),所以你應(yīng)該按照統(tǒng)一的命名空間前綴法則,用類(lèi)名前綴作為這個(gè)通知名字的前綴。(通常在頭文件中對(duì)外提供的常量都需要加上前綴,聲明extern + const,并且并不是在頭文件中定義,而是在實(shí)現(xiàn)文件中定義。如果不是對(duì)外公開(kāi)的常量,那么通常直接在實(shí)現(xiàn)文件里聲明為static + const,并且也要加上前綴,直接在后面進(jìn)行定義。)
同時(shí),用一個(gè) Did/Will 這樣的動(dòng)詞以及用 "Notifications" 后綴來(lái)命名這個(gè)通知也是一個(gè)好的實(shí)踐。
// Foo.h
extern NSString * const ZOCFooDidBecomeBarNotification
// Foo.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";
- Objective-C編程中語(yǔ)句和變量的一些編寫(xiě)規(guī)范建議
- 舉例講解Objective-C中@property屬性的用法
- 詳解Objective-C編程中對(duì)設(shè)計(jì)模式中適的配器模式的使用
- 詳解Objective-C設(shè)計(jì)模式編程中對(duì)備忘錄模式的運(yùn)用
- 淺析Objective-C中分類(lèi)Category的使用
- Objective-C的NSOperation多線程類(lèi)基本使用指南
- 簡(jiǎn)介Objective-C解析XML與JSON數(shù)據(jù)格式的方法
- 簡(jiǎn)單講解Objective-C的基本特性及其內(nèi)存管理方式
- 理解Objective-C的變量以及面相對(duì)象的繼承特性
- 以實(shí)例講解Objective-C中的KVO與KVC機(jī)制
相關(guān)文章
iOS Xcode8更新后輸出log日志關(guān)閉的方法
今天剛把xcode更新到了xcode8,運(yùn)行發(fā)現(xiàn)好多l(xiāng)og輸出,怎么關(guān)閉呢,不是很清楚,通過(guò)查閱相關(guān)資料順利關(guān)掉這些log日志,下面小編把方法共享下,需要的朋友參考下2016-09-09iOS實(shí)現(xiàn)不規(guī)則Button點(diǎn)擊效果實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)不規(guī)則Button點(diǎn)擊的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位iOS開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04IOS 中UIKit-UIPageControl利用delegate定位圓點(diǎn)位置
這篇文章主要介紹了IOS 中UIKit-UIPageControl利用delegate定位圓點(diǎn)位置 的相關(guān)資料,需要的朋友可以參考下2017-04-04詳解iOS開(kāi)發(fā) - 用AFNetworking實(shí)現(xiàn)https單向驗(yàn)證,雙向驗(yàn)證
這篇文章主要介紹了詳解iOS開(kāi)發(fā) - 用AFNetworking實(shí)現(xiàn)https單向驗(yàn)證,雙向驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12iOS開(kāi)發(fā)多線程下全局變量賦值崩潰原理詳解
這篇文章主要為大家介紹了iOS開(kāi)發(fā)多線程下全局變量賦值崩潰原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07iOS當(dāng)多個(gè)網(wǎng)絡(luò)請(qǐng)求完成后執(zhí)行下一步的方法詳解
在多線程中,有時(shí)候我們會(huì)遇到一個(gè)界面同時(shí)有多個(gè)網(wǎng)絡(luò)請(qǐng)求(比如a,b,c,d四個(gè)網(wǎng)絡(luò)請(qǐng)求),在這四個(gè)個(gè)請(qǐng)求結(jié)束后,在請(qǐng)求到數(shù)據(jù)去做其他操作(UI更新等),下面這篇文章主要給大家介紹了關(guān)于iOS當(dāng)多個(gè)網(wǎng)絡(luò)請(qǐng)求完成后執(zhí)行下一步的相關(guān)資料,需要的朋友可以參考下。2017-12-12IOS 開(kāi)發(fā)之UILabel 或者 UIButton加下劃線鏈接
這篇文章主要介紹了IOS 開(kāi)發(fā)之UILabel 或者 UIButton加下劃線鏈接的相關(guān)資料,需要的朋友可以參考下2017-07-07