標籤:
(1)block
- (void)viewDidLoad { [super viewDidLoad]; __block int a=10; NSLog(@"a=%d",a); void (^blockName)()=^{ a=20; }; NSLog(@"a=%d",a); blockName(); NSLog(@"a=%d",a);}
——以上輸出結果是10,10,20。
——只要在變數前面增加__block,在block裡面就可以修改該變數的值。當然也有其他方法如添加static等。
——如何能實現,需要查看底層代碼,也就是用C語言寫成的運行時代碼。在終端利用clang -rewrite-objc .m檔案名稱,把檔案轉為.cpp的C++底層代碼,可以分析底層實現的原理,可以用open .cpp檔案名稱,開啟查看。
——核心原理是,因為有一個__forwarding參數,每次輸出都是調用a.__forwarding->a的值。而且block本質上就是一個指向結構體的地址。
(2)運行時,平時我們寫得代碼其實最終都會轉成運行時代碼,效率快。但是我們一般用OC寫。如果非要使用運行時代碼方式書寫,可以增加下面的類。
#import <objc/message.h>//需要用到發送訊息的時候,設定函數的時候#import <objc/runtime.h>//裡面有一些特殊的函數
我們平時使用的方法,其實就是訊息機制,用得就是objc_msgSend這個函數。我們只要包含了上面的標頭檔<objc/message.h>,也可以用這個函數寫代碼。
——使用價值之一。我們平時的分類一般只能擴充一個類的方法,而不能擴充它的成員屬性。而使用的某些方法(如下)就可以為類動態地擴充成員屬性。
static double heightKey;-(void)setHeight:(double)height{ objc_setAssociatedObject(self, &heightKey, @(height), OBJC_ASSOCIATION_ASSIGN);}-(double)height{ return [objc_getAssociatedObject(self, &heightKey) doubleValue];}
——遍曆類的成員變數(使用者就是可以遍曆出所有變數統一操作,比如encode或者decode之類的操作)
#import "ViewController.h"#import "Person.h"#import <objc/message.h>#import <objc/runtime.h>@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; unsigned int count=0; //可以獲得類的所有成員屬性,預設指向0,即類的第一個成員屬性 Ivar *ivars=class_copyIvarList([Person class], &count); //遍曆成員變數 for (int i=0; i<count; i++) { Ivar ivar=ivars[i]; const char *name=ivar_getName(ivar); const char *type=ivar_getTypeEncoding(ivar); NSLog(@"%s,%s",name,type); }}
輸出結果是:
_age,i_name,@"NSString"
(3)block變數定義時為什麼用copy?block是放在哪裡的?
——預設情況下,block是存檔在棧中,可能被隨時回收,需要copy操作。這也就是我們在定義block的時候用得時copy。而不是weak等等。
//預設是放在棧中,可能會被隨時銷毀 void (^blockName)()=^{ }; //進行一次copy操作,就可以放在堆中了。 //[blockName copy]; //以下方法也一樣。但是只能在非ARC中使用。 //Block_copy(blockName); //用retain沒有用的原因:retain只是增加一次計數,block記憶體還是在棧中,並沒有轉移到堆中。
——block如果是copy的話,裡面使用它所在的類的對象的話,這個對象永遠無法被釋放。即person對象在堆中是強指標,且person在blockName就在,而blockName指向的代碼也在堆中,即它的person.age=20的那些代碼也在堆中,而這些代碼中有person對象,所以又反過來指向person對象。就這麼在相互指向的,永遠不能釋放。
Person *person=[[Person alloc]init]; person.blockName=^{ person.age=20; };
解決辦法如下。此處還不能直接把__unsafe_unretained放在建立person對象的那一行。因為如果那樣的話,這個person對象是一個弱指標指向的,一出生就死了。引入弱指標person0的目的就是block代碼裡的person0回指對象時,是弱引用,這樣就不會出現2個強引用互相指著。也可以用__weak。
Person *person=[[Person alloc]init]; __unsafe_unretained Person *person0=person; person.blockName=^{ person0.age=20; };
【iOS開發-117】block為什麼用copy?利用runtime運行時的objc_方法為分類擴充成員變數