NSObject * __weak someObject = [[NSObject alloc] init];, 這個someObject沒有對象強引用他, 所以這行代碼之后會立馬被置為nil, NSObject * __weak someObject = self.someObject, 這個someObject在這行代碼之后不會立刻被置為nil, 而是會在所在的代碼塊結束后被置為nil。
oc中的對象是動態(tài)管理(內存)的, 是分配在heap(堆)上所以需要一個指針來指向它(才能訪問), 所以對象類型需要用 星號 NSString * str;
比較是否相同: 使用 if(a==b) {}, 如果a,b是對象類型, 那么比較的是指針是否相同, 而不是比較值是否相同, 如果a, b是基本類型(int, double...), 那么比較的是值是否相同; 使用if ([a isEqual: b]) { }, 則比較的是a,b的值是否相同。
4. 初始化基本類型的時候盡量設置初始值, 因為編譯器分配的初始值并不確定, 但是對象類型會默認初始化為nil。
oc中屬性的getter和setter@property (nonatomic) NSString *name;
例如當有這樣一個name屬性的時候, 默認是readWrite的, 編譯器會自動生成一個set (setName:)和get(-(NSString *)name)方法, 這個時候可以通過set或者get方法訪問到name, 如果申明為(readonly), 那么將只會生成get方法
[self setName:@"set name"];
NSString *getName = [self name];
也可以通過點語法訪問(實際上是會自動調用set和get方法)
self.name = @"set name";
NSString *dotName = self.name;
同時你可以重寫name的get(懶加載...)和setter(攔截set方法)...對應name屬性, 編譯器會生成(synthesize)一個 _name 允許我們直接通過指針訪問變量, 而不會調用get方法, 所以通過_xx訪問的變量不會調用懶加載(get方法), 所以在寫懶加載方法的時候, 不能使用self.xx(造成死循環(huán)), 而要使用_xx -
(NSString *)name {
// 這里面不能使用self.name , 因為點語法會調用這個get方法, 造成死循環(huán)
if (_name == nil) {
_name = @"name";
}
return _name;
}
同時這個synthesize的名字我們是可以自己修改的, 使用如下的語法@synthesize name = customName;
那么這個時候就不能通過 __name訪問到name了, 因為我們已經(jīng)指定了通過customName才能訪問到了NSString *getName = customName;
當然如果, 你是這樣寫的 @synthesize name;, 并沒有指定名字, 這個時候訪問的時候就直接使用變量名而不需要加下劃線( _ )了 name = @"set name"; ??這個時候就比較爽了, 和swift,java這些一樣, 不需要self,this了;
5,分類(category)定義的函數(shù)和屬性在運行時中和原生的class中定義的東西并沒有區(qū)別At runtime, there’s no difference between a method added by a category and one that is implemented by the original class
6,不過分類中定義的屬性, 編譯器并不會自動生成getter和setter, 以及_XX變量來訪問,需要自己提供getter和setter, 并且需要使用運行時才能綁定這個屬性到這個類中, 實現(xiàn)原生類中定義的屬性的效果
///例如可能是這樣的使用
static const void *propertyKey = &propertyKey;
/// 將value通過運行時綁定到self
objc_setAssociatedObject(self, propertyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
/// 將value在運行時中通過propertyKey取出綁定的值
id value = objc_getAssociatedObject(self, propertyKey);
7,同時分類也可以用來將一個復雜的類中的代碼分塊(swift的extension可以有相似的作用), 使得代碼組織更好, 例如可以將tableView的delegate, 和Datasource在分類中實現(xiàn)。
@implementation ViewController(tableview)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
@end
8,但是在使用category來擴展Cocoa的原生類的時候, 要注意函數(shù)的命名如果是和原生已有的函數(shù)名相同,那么將會發(fā)生不可預料的結果(不能確定哪一個方法在運行時會被調用), 因此建議在自己的函數(shù)名前面加上前綴, 就像重寫 +load() 來實現(xiàn)各種黑魔法的時候也是可能會發(fā)生不可預料的結果, 因為同一個項目中可能有多個地方重寫了這個類的 +load方法
9,初始化NSArray的時候, 如果通過NSArray *arr1 = @[object1, object2];, 不需要以nil結尾, 如果通過構造方法初始化, 則需要傳入nil結尾, 同時, 如果中間的對象有nil, 那么將在中間nil就結束了, NSArray *arr2 = [NSArray arrayWithObjects:object1, object2, object3, nil, object4, object5, nil] 這個arr2只可能會存儲第一個nil前的對象
10,如果在數(shù)組中一定要存儲nil, 那么只能用NSNull來代替
11,如果NSArray中存儲的是NSArray, NSDictionary, NSString, NSData, NSDate , NSNumber這些類型的對象, 那就可以直接寫入disk并且讀取disk的數(shù)據(jù)做持久化數(shù)據(jù)操作[array writeToURL:fileURL atomically:YES], 但是如果是有其他的類型, 就需要使用歸檔來實現(xiàn)了
12,在oc中block是object類型的, 所以是可以存儲在NSArray...中, 同時在調用block的時候, 如果block為nil(未賦值), 那么程序將crash.
13,oc中block可以捕獲變量, 什么意思呢 --- 就是block會默認捕獲到變量的值, 在之后不受到原來變量的改變的影響, 例如
int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock(); ---- 輸出的值仍然為 42
14,第二種block捕獲變量的方式, 是捕獲變量的指針, 被捕獲的變量值改變, 則block中的變量值也改變了,不過需要對變量進行__block標記, 例如上面的代碼, 只改變一點, 結果就變了
__block int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock(); --- 輸出值這時是 84
15,preferredMaxLayoutWidth用來制定最大的寬,一般用在多行的UILabel中
systemLayoutSizeFittingSize方法能夠獲得view的高度
iOS7有兩個很有用的屬性,topLayoutGuide和bottomLayoutGuide,這個兩個主要是方便獲取UINavigationController和UITabBarController的頭部視圖區(qū)域和底部視圖區(qū)域。
16,AutoLayout關于更新的幾個方法的區(qū)別:
setNeedsLayout:告知頁面需要更新,但是不會立刻開始更新。執(zhí)行后會立刻調用layoutSubviews。
layoutIfNeeded:告知頁面布局立刻更新。所以一般都會和setNeedsLayout一起使用。如果希望立刻生成新的frame需要調用此方法,利用這點一般布局動畫可以在更新布局后直接使用這個方法讓動畫生效。
layoutSubviews:系統(tǒng)重寫布局
setNeedsUpdateConstraints:告知需要更新約束,但是不會立刻開始
updateConstraintsIfNeeded:告知立刻更新約束
updateConstraints:系統(tǒng)更新約束.
17,AutoLayout情況如何計算UITableView的變高高度
主要是UILabel的高度會有變化,所以這里主要是說說label變化時如何處理,設置UILabel的時候注意要設置preferredMaxLayoutWidth這個寬度,還有ContentHuggingPriority為UILayoutPriorityRequried
CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 10 * 2;
textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.preferredMaxLayoutWidth = maxWidth;
[self.contentView addSubview:textLabel];
[textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(statusView.mas_bottom).with.offset(10);
make.left.equalTo(self.contentView).with.offset(10);
make.right.equalTo(self.contentView).with.offset(-10);
make.bottom.equalTo(self.contentView).with.offset(-10);
}];
[_contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
如果版本支持最低版本為iOS 8以上的話可以直接利用UITableViewAutomaticDimension在tableview的heightForRowAtIndexPath直接返回即可。
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 80; //減少第一次計算量,iOS7后支持
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// 只用返回這個!
return UITableViewAutomaticDimension;
}
但如果需要兼容iOS 8之前版本的話,就要回到老路子上了,主要是用systemLayoutSizeFittingSize來取高。步驟是先在數(shù)據(jù)model中添加一個height的屬性用來緩存高,然后在table view的heightForRowAtIndexPath代理里static一個只初始化一次的Cell實例,然后根據(jù)model內容填充數(shù)據(jù),最后根據(jù)cell的contentView的systemLayoutSizeFittingSize的方法獲取到cell的高。具體代碼如下
//在model中添加屬性緩存高度
@interface DataModel : NSObject
@property (copy, nonatomic) NSString *text;
@property (assign, nonatomic) CGFloat cellHeight; //緩存高度
@end
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static CustomCell *cell;
//只初始化一次cell
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])];
});
DataModel *model = self.dataArray[(NSUInteger) indexPath.row];
[cell makeupData:model];
if (model.cellHeight <= 0) {
//使用systemLayoutSizeFittingSize獲取高度
model.cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
}
return model.cellHeight;
}
18,UILayoutFittingCompressedSize 表示返回最小可能的值
UILayoutFittingExpandedSize 表示返回最大可能的值
19,使用masonry進行l(wèi)abel的多行顯示設置時,主要是如下兩個參數(shù)的設置
1、@property(nonatomic)CGFloat preferredMaxLayoutWidth
2、- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis
//如下
UILabel *label3 = [[UILabelalloc] initWithFrame:CGRectZero];
[self.viewaddSubview:label3];
label3.backgroundColor = [UIColorcolorWithWhite:0.5alpha:0.3];
label3.text =@"Masonry是一個輕量級的布局框架與更好的包裝AutoLayout語法。Masonry有它自己的布局方式,描述NSLayoutConstraints使布局代碼更簡潔易讀。Masonry支持iOS和Mac OS X。Masonry github地址:https://github.com/SnapKit/Masonry";
label3.preferredMaxLayoutWidth = (WidthScreen -10.0 * 2);
[label3 setContentHuggingPriority:UILayoutPriorityRequiredforAxis:UILayoutConstraintAxisVertical];
label3.numberOfLines =0;
[label3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(10.0);
make.right.mas_equalTo(-10.0);
make.top.mas_equalTo(10.0);
}];
分類
Category的作用
可以在不修改原來類的基礎上, 為這個類擴充一些方法
一個龐大的類可以分模塊開發(fā)
一個龐大的類可以由多個人來編寫,更有利于團隊合作
Category的使用注意:
分類只能增加方法,不能增加成員變量
在分類方法的實現(xiàn)中可以訪問原來類的成員變量
分類中可以重新實現(xiàn)原來類的方法,但是會覆蓋原來的方法
方法的調用優(yōu)先級:分類->原來的類->父類
類的啟動過程
+load方法
在程序啟動的時候會加載所有的類和分類,并調用所有類和分類的+load方法(只會調用一次)
先加載父類,再加載子類;也就是先調用父類的+load,再調用子類的+load
先加載元原始類,再加載分類
不管程序運行過程有沒有用到這個類,都會調用+load加載
+initialize
在第一次使用某個類時(比如創(chuàng)建對象等),只會調用一次+initialize方法
一個類只會調用一次+initialize方法,先調用父類的,再調用子類的
打開文件共享權限:在Info.plist文件中添加Application supports iTunes file sharing ,并設置為YES,即可通過iTunes直接導出來
手機上自動安裝應用:
`itms-services://?action=download-manifest&url=https://www.xxxxx.com/abc......`