最近在看一些iOS的進階書籍,作一些簡單的筆記來加深印象。 此次讀的是《Effective+Objective-C 2.0 編寫高質量iOS與OS X代碼的52個有效方法》。程序員
注:20170719對文章作了一些修正算法
Objective-C語言由Smalltalk語言演化而來,而Smalltalk是消息型語言的鼻祖。編程
//消息型語言
Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];
//函數型語言
Object *obj = new Object;
obj->preform(parameter1,parameter2);
複製代碼
注:「class-continutation分類」說的就是「擴展」。 我的理解:少引入無用頭文件的做用: 1)減小程序編輯時間 2)下降類之間的耦合,使類更清晰,讓類的使用者更容易理解 3)有效避免相互引用的問題數組
NSNumber *someNumber = @(1);
NSArray *animals = @[@"dog",@"cat",@"mouse",@"badger"];
//取下標操做
NSString *dog = animal[1];
NSDictionary *personData = @{@"firstName":@"shi",@"lastName":@"xueqian",age:@(26)};
NSString *lastName = personData[@"lastName"];
//可變數組和字典
[mutableArray replaceObjectAtIndex:1 withObject:@"dog"];
[mutableDictionary setObject:@"xueqian" forKey:@"lastName"];
//可用字面量語法來替換
mutableArray[1] = @"dog";
mutableDictonary[@"lastName"] = @"xueqian";
複製代碼
//預處理指令
#define ANIMATION_DURATION 0.3
//常量定義
static const NSTimeInterval kAnimationDuration = 0.3;
//全局常量 頭文件中 聲明
extern NSString *const EOCStringConstant;
//全局常量 實現文件中 定義
NSString *const EOCStringConstant = @"VALUE";
複製代碼
//普通枚舉
typedef NS_ENUM(NSUInteger, EOCConnectionState){
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
};
//使用枚舉做爲參數時,switch語句最好不要加defalut分支
switch(_currentState) {
case:EOCConnectionStateDisconnected:
//幹活
break;
case:EOCConnectionStateConnecting:
//幹活
break;
case:EOCConnectionStateConnected:
//幹活
break;
}
//二進制枚舉
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection){
EOCPermittedDirectionUp = 1 << 0,
EOCPermittedDirectionDown = 1 << 1,
EOCPermittedDirectionLeft = 1 << 2,
EOCPermittedDirectionRight = 1 << 3,
}
//二進制枚舉使用
EOCPermittedDirection direction = EOCPermittedDirectionUp | EOCPermittedDirectionDown;
if (direction & EOCPermittedDirectionUp){
//有設置 EOCPermittedDirectionUp
}
複製代碼
###第6條:理解「屬性」這一律念緩存
Objective-C對象一般會把其所須要的數據保存爲各類實例變量。實例變量通常經過「存取方法」來訪問。其中,「獲取方法(getter)」用於讀取變量值,而「設置方法」(setter)用於寫入變量值。安全
@synthesize語法:指定實例變量的名字(較少用):bash
@implementation EOCPerson
@synthesize firstName = _myFirstName;
@end
複製代碼
@dynamic關鍵字:它會告訴編譯器,不要自動建立實現屬性所用的實例變量,也不要爲其建立存取方法。並且,在編譯訪問屬性的代碼時,即便編譯器發現沒有定義存取方法,也不會報錯,它相信這些方法能在運行期找到。網絡
@implementation EOCPerson
@dynamic firstName,lastName;
@end
複製代碼
原子性:默認atomic屬性。能夠經過鎖定機制來確保getter方法操做的原子性。可是並不能保證「線程安全」。因爲iOS中使用同步鎖開銷太大,通常只使用nonatomic。 讀/寫權限:readonly和readwrite getter=:指定getter的方法名。數據結構
@property (nonatomic, getter=isOn) BOOL on;
複製代碼
關聯類型 | 等效的@property屬性 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic retain |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic copy |
OBJC_ASSOCIATION_RETAIN | retain |
OBJC_ASSOCIATION_COPY | copy |
//此方法以給定的鍵和策略爲某對象設置關聯對象值
void objc_setAssociatedObject(id object, void *key, id value, objc_associationPolicy policy)
//此方法經過給定的鍵從某對象中獲取關聯對象的值
void objc_getAssociatedObject(id object, void *key)
//此方法移除某對象的所有關聯對象
void objc_removeAssociatedObject(id object)
複製代碼
- C語言:C語言使用「靜態綁定」,也就是說,在編譯期就能決定運行時所調用的函數。
//給對象發送消息
id returnValue = [someObject messageName:parameter];
//objc_msgSend原型
void objc_msgSend(id self, SEL _cmd, ...)
//給對象發送消息底層
id returnValue = objc_msgSend(some Object, @selector(messageName:),parameter);
複製代碼
//方法交換
void method_exchangeImplementations(Method m1, Method m2)
//方法實現
Method class_getInstanceMethod(Class aClass, SEL aSelector)
//demo,交換lowercaseString和uppercaseString方法
Method originalMethod = class_getInstanceMethod([NSString class],
@selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));
method_exchangeImplementations(originalMethod,swappedMethod);
複製代碼
isMemberOfClass:可以判斷出對象是否爲某個類的實例
isKindOfClass:可以判斷出對象是否爲某類或其派生類的對象
複製代碼
Objective-C沒有其餘語言那種內置的命名空間機制。鑑於此,咱們在起名時須要避免潛在的命名衝突。 避免此問題的惟一辦法是變相實現命名空間:爲全部名稱都加上適當前綴。多線程
- (NSString *)description {
return [NSString stringWithFormat:@"<%@:%p %@",[self class], self, @{@"firstName":_firstName, @"lastName":_lastName}];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@ %@",_firstName,_lastName];
}
- (NSString *)debugDescription {
return [NSStrig stringWithFormat:@"< %@ ,%p %@ ,%@",[self class], self, _firstName, _lastName];
}
複製代碼
這裏指的可變是「readwrite」屬性對象 不可變是「readonly」屬性對象
給方法命名時的注意事項:
NSError對象裏封裝了三條信息:
- (BOOL)doSomething:(NSError **)error {
if (/* there was an error */) {
if (error) {
*error = [NSError errorWithDomain:domain code:code userInfo:userInfo];
}
return NO;
else {
return YES;
}
}
NSError *error = nil;
BOOL ret = [object doSomethig:&error];
if (error) {
}
複製代碼
第22條:理解NSCopying協議
//一個類支持拷貝功能須要實現 NSCopying協議只有這一個方法。其中NSZone目前只有一個默認值,可無論。
- (id)copyWithZone:(NSZone *)zone
- (id)copyWithZone:(NSZone *)zone {
EOCPerson *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName andLastName:_lastName];
return copy;
}
複製代碼
對象把應對某個行爲的責任委託給另一個類了。 常規的委託模式:信息從類流向受委託者(delegate)。 數據源模式:信息從數據源(Data Source)流向類。
注:這裏的「class-continuation分類」其實就是咱們日常所說的「擴展」。
從Mac OS X 10.8開始,「垃圾收集器」已經正式廢棄了,以Objective-C代碼編寫Mac OS X程序時不該再使用它,而iOS則從未支持過垃圾收集。
不能在ARC模式下調用
retain
,release
,autorelease,
dealloc方法 以
alloc,
new,
copy,
mutableCopy``開頭的方法,返回的對象歸調用者全部(返回的對象保留計數會+1)。
dealloc方法毫不能主動調用。
iOS系統會自動建立一些線程,這些線程默認都有自動釋放池,每次執行「事件循環」(event loop)時,就會將其清空。 自動釋放池的範圍:左括號到右括號({自動釋放池範圍})。在該範圍內的對象,將會在末尾處收到release消息。
//塊的聲明語法結構
return_type (^block_name)(parameters)
//塊的定義語法結構:
^ return_type (parameters){函數體},其中``return_type``和``parameters``均可以省略。即^{}
複製代碼
塊的強大之處是:在聲明它的範圍裏,全部變量均可覺得其所捕獲。也就是說,那個範圍內的所有變量,在塊裏依然可用。 若是塊所捕獲的變量是對象類型,那麼就會自動保留它。 定義塊的時候,其所佔的內存區域是分配在棧中的。也就是說,塊只在定義它的那個範圍內有效。 爲解決此問題,可給塊對象發送copy消息以拷貝之。這樣的話,就能夠把塊從棧複製到堆了。 全局塊;不會捕捉任何狀態(好比外圍的變量等),運行時也無須有狀態來參與。
//塊類型的語法結構:
return_type (^block_name)(parameters)
//typedef
typedef return_type(^block_name)(parameters);
複製代碼
委託模式有個缺點:若是類要分別使用多個獲取器下載不一樣數據,那麼就得在delegate回到方法里根據傳入的獲取器來切換。
//能夠在運行時調用方法
- (id)performSelector:(SEL)selector
//可帶一個參數
- (id)performSelector:(SEL)selector withObject:(id)object
//可帶兩個參數
- (id)performSelector:(SEL)selector withObject:(id)object withObject:(id)object
//可延時執行方法
- (void)performSelector:(SEL)selector withObject:(id)argument afterDelay:(NSTimeInterval)delay
//可放到另外一個線程中執行
- (void)performSelector:(SEL)selector onThread:(NSThread *)thread withObject:(id)argument waitUntilDone:(BOOL)wait
- (void)performSelectorOnMainThread:(SEL)selector withObject:(id)argument waitUntilDone:(BOOL)wait
//延後執行方法的兩種實現方式:
[self performSelector:@selector(doSomething) withObject:nil afterDelay:5.0];
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){
[self doSomething];
});
//把任務放在主線程執行的兩種方式
[self performSlectorOnMainThread:@selector(doSomething) withObject:nil waitUntilDone:NO];
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
複製代碼
NSOperationQueue,開發者能夠把操做以NSOperation子類的形式放在隊列中,而這些操做也可以併發執行。 GCD是純C的API,而NSOperationQueue則是Objective-C的對象。 用NSOperationQueue類的「addOerationWithBlock:」方法搭配NSBlockOperation類來使用操做隊列,其語法與純GCD很是相似。 NSOperationQueue與NSOperation類的好處以下:
dispatch group是GCD的一項特性,可以把任務分組。調用者能夠等待這組任務執行完畢,也能夠在提供回調函數以後繼續往下執行,這組任務完成時,調用者會獲得通知。
//建立dispatch group
dispatch_group_t dispatch_group_create();
//把任務編組(普通dispatch_async的變體)
void dispatch_group_asunc(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
//指定任務所屬的dispatch_group
void dispatch_group_enter(dispatch_group_t group);
void dispatch_group_leave(dispatch_group_t group);
//等待dispatch_group執行完畢(timeout能夠取常量DISPATCH_TIME_FOREVER,表示函數一致等待dispatch_group執行完,而不會超時)
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
//等待dispatch_group執行完畢以後執行block
void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
複製代碼
void dispatch_once (dispatch_once_t *token, dispatch_block_t block);
+ (id)sharedInstance {
static EOCClass *sharedInstance = nil;
static icdispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
複製代碼
使用dispatch_once能夠簡化代碼而且完全保證線程安全,開發者根本無需擔憂加鎖或同步。 因爲每次調用時都必須使用徹底相同的標記,因此標記要聲明成static。 把該變量定義在static做用域中,能夠保證編譯器在每次執行sharedInstance方法時都會複用這個變量,而不會建立新變量。
將一系列代碼封裝爲動態庫,並在其中放入描述其接口的頭文件,這樣作出來的東西就叫框架。有時爲iOS平臺構建的第三方框架所使用的是靜態庫,這是由於iOS應用程序不容許在其中包含動態庫。這些東西嚴格來說並非真正的框架,然而也常常視爲框架。不過,全部iOS平臺的系統框架仍然使用動態庫。 在爲Mac OS X或iOS系統開發「帶圖形界面的應用程序」時,會用到名爲Cocoa的框架,在iOS上成爲Cocoa Touch。其實Cocoa自己並非框架,可是裏面集成了一批建立應用程序時常常會用到的框架。
第48條:多用塊枚舉,少用for循環
//for循環遍歷
NSArray *arr1 = @[@1,@2,@3,@4,@5];
for (int i = 0; i < arr1.count; ++i) {
NSLog(@"arr1[i]=%@",arr1[i]);
}
//for循環反向遍歷
for (NSInteger i = arr1.count-1; i >= 0; --i) {
NSLog(@"arr1[i]=%@",arr1[i]);
}
//NSEnumerator遍歷法
NSArray *arr1 = @[@1,@2,@3,@4,@5];
NSEnumerator *enumerator = [arr1 objectEnumerator];
id object;
while ((object = [enumerator nextObject]) != nil) {
NSLog(@"object=%@",object);
}
//NSEnumerator遍歷法反向遍歷
NSEnumerator *reverseenu = [arr1 reverseObjectEnumerator];
id object1;
while ((object1 = [reverseenu nextObject]) != nil) {
NSLog(@"object1=%@",object1);
}
//快速遍歷法
NSArray *arr1 = @[@1,@2,@3,@4,@5];
for (NSObject *obj in arr1) {
NSLog(@"obj=%@",obj);
}
//快速遍歷法反向遍歷
for (NSObject *obj1 in [arr1 reverseObjectEnumerator]) {
NSLog(@"obj1=%@",obj1);
}
//塊枚舉法
NSArray *arr1 = @[@1,@2,@3,@4,@5];
[arr1 enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"idx=%zd,obj=%@",idx,obj);
}];
//塊枚舉法反向遍歷
[arr1 enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"idx=%zd,obj=%@",idx,obj);
}];
複製代碼
Foundation框架中的Objective-C類所具有的某些功能,是CoreFoundation框架中的C語言數據接口所不具有的,反之亦然。 __bridge:ARC仍然具有這個Objective對象的全部權。 __bridge_retained:ARC將交出對象的全部權。 __bridge_transfer:C轉化爲OC
當程序啓動的時候,類和分類,一定會調動且僅調用一次load方法。 先調用類的load方法,再調用分類的load方法。 先調用超類的load方法,再調用子類的load方法。 沒法判斷出各個類的載入順序。 load方法須要實現得精簡一些,由於整個應用程序會在執行load方法時都會阻塞。 initialize方法會在程序首次用該類以前調用,且只調用一次。 initialize是「懶加載」的,若是某個類一直都沒有使用,就不會執行該類的initialize方法。 initialize方法能夠安全使用並調用任意類中的任意方法。 initialize方法只應該用來設置內部數據,不該該在其中調用其餘方法。
NSTimer對象會保留其目標,也就是說, NSTimer對象會對目標對象進行強引用。 一次性計時器:只執行一次任務,以後自動失效 重複執行模式:重複執行任務,必須本身調用invalidate方法,才能令其中止。 只有把計時器放在運行循環裏,它才能正常觸發任務。