2012-02-29 27 views
17

Tôi có một danh mục trên NSObject có nghĩa vụ phải có một số nội dung. Khi tôi gọi nó trên một đối tượng, tôi muốn ghi đè lên phương thức dealloc của nó để làm một số dọn dẹp.Xáo trộn một cá thể đơn lẻ, không phải là một lớp

Tôi muốn làm điều đó bằng cách sử dụng phương pháp nổi loạn, nhưng không thể tìm ra cách. Các ví dụ duy nhất tôi đã tìm thấy là làm thế nào để thay thế việc thực hiện phương pháp cho toàn bộ lớp (trong trường hợp của tôi, nó sẽ ghi đè lên dealloc cho TẤT CẢ NSObjects - mà tôi không muốn).

Tôi muốn ghi đè phương thức dealloc của các phiên bản cụ thể của NSObject.

@interface NSObject(MyCategory) 
-(void)test; 
@end 

@implementation NSObject(MyCategory) 
-(void)newDealloc 
{ 
    // do some cleanup here 
    [self dealloc]; // call actual dealloc method 
} 
-(void)test 
{ 
    IMP orig=[self methodForSelector:@selector(dealloc)]; 
    IMP repl=[self methodForSelector:@selector(newDealloc)]; 
    if (...) // 'test' might be called several times, this replacement should happen only on the first call 
    { 
    method_exchangeImplementations(..., ...); 
    } 
} 
@end 

Trả lời

18

Bạn không thể thực sự làm điều này vì các đối tượng không có bảng phương thức riêng của chúng. Chỉ có các lớp có các bảng phương thức và nếu bạn thay đổi chúng, nó sẽ ảnh hưởng đến mọi đối tượng của lớp đó. Có một cách đơn giản xung quanh điều này mặc dù: Thay đổi lớp của đối tượng của bạn trong thời gian chạy thành một lớp con được tạo động. Kỹ thuật này, còn được gọi là isa-swizzling, được Apple sử dụng để thực hiện KVO tự động.

Đây là một phương pháp mạnh mẽ và có cách sử dụng. Nhưng đối với trường hợp của bạn có một phương pháp dễ dàng hơn bằng cách sử dụng các đối tượng liên quan. Về cơ bản, bạn sử dụng objc_setAssociatedObject để kết hợp một đối tượng khác với đối tượng đầu tiên của bạn mà làm sạch trong dealloc của nó. Bạn có thể tìm thêm thông tin chi tiết trong this blog post on Cocoa is my Girlfriend.

+0

Cảm ơn câu trả lời. Vì vậy, về cơ bản, cho mỗi cuộc gọi - tôi phân bổ một đối tượng mới (mà tôi thực hiện) và thiết lập nó như là đối tượng liên quan và tôi làm sạch bên trong dealloc của nó? –

+0

Chính xác. Nhìn vào bài đăng trên blog đó, nó thậm chí còn có một danh mục tốt đẹp trên 'NSObject' cho phép bạn đăng ký các khối được gọi trong suốt quá trình dealloc của bất kỳ đối tượng nào. – Sven

8

Lựa chọn phương pháp dựa trên lớp của một đối tượng đối tượng, do đó, phương pháp gây phiền nhiễu ảnh hưởng đến tất cả các phiên bản của cùng một lớp - như bạn đã khám phá.

Nhưng bạn có thể thay đổi lớp học của một cá thể, nhưng bạn phải cẩn thận! Dưới đây là phác thảo, giả sử bạn có một lớp:

@instance MyPlainObject : NSObject 

- (void) doSomething; 

@end 

Bây giờ nếu cho một số trường hợp MyPlainObject bạn muốn thay đổi hành vi của doSomething đầu tiên bạn xác định một lớp con:

@instance MyFancyObject: MyPlainObject 

- (void) doSomething; 

@end 

Bây giờ, bạn có thể tạo ra các trường hợp MyFancyObject rõ ràng, nhưng những gì chúng tôi cần thực hiện là phiên bản đã có từ trước của MyPlainObject và đặt thành MyFancyObject để chúng tôi có được hành vi mới. Cho rằng chúng ta có thể sự gian lận của lớp, thêm dòng sau vào MyFancyObject:

static Class myPlainObjectClass; 
static Class myFancyObjectClass; 

+ (void)initialize 
{ 
    myPlainObjectClass = objc_getClass("MyPlainObject"); 
    myFancyObjectClass = objc_getClass("MyFancyObject"); 
} 

+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy 
{ 
    object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass); 
} 

Bây giờ cho bất kỳ gốc thể hiện của MyPlainClass bạn có thể chuyển sang hoạt động như một MyFancyClass, và ngược lại:

MyPlainClass *mpc = [MyPlainClass new]; 

... 

// masquerade as MyFancyClass 
[MyFancyClass changeKind:mpc fancy:YES] 

... // mpc behaves as a MyFancyClass 

// revert to true nature 
[MyFancyClass changeKind:mpc: fancy:NO]; 

(Một số) cảnh báo:

Bạn có thể chỉ làm điều này nếu phân lớp ghi đè hoặc thêm phương thức và thêm static biến (lớp).

Bạn cũng cần một lớp con cho lớp học bao giờ bạn muốn thay đổi hành vi của, bạn không thể có một lớp duy nhất có thể thay đổi hành vi của nhiều lớp khác nhau.

+0

Nhưng trong trường hợp đó, tôi phải biết trước những gì sẽ là lớp của trường hợp đó. Trong trường hợp của tôi, đối tượng có thể là bất cứ thứ gì (ngay cả đối tượng được tạo bởi chính SDK), vì vậy tôi không thể cho rằng tôi sẽ biết loại đó. –

+0

Vâng, đó là một trong những điều cần biết! Nếu đó là cần thiết đi các tuyến đối tượng liên quan. Nhưng nếu bạn đang nhắm mục tiêu các lớp học cụ thể thì bạn sẽ thấy một chút (chủ quan của khóa học) đơn giản hơn các đối tượng liên kết và tổng quát hơn vì bạn có thể ghi đè lên bất kỳ phương thức nào một cách dễ dàng. – CRD

+0

Mặc dù tôi đã sử dụng một đối tượng liên quan làm giải pháp của mình, tôi đánh giá cao câu trả lời của bạn. Tôi có một câu hỏi về nó: vì nó chỉ hữu ích để ghi đè/thêm các phương thức - khác nhau giữa cái này và danh mục là gì? –

1

Tôi đã tạo một API nổi bật, đồng thời cũng nổi bật với sự kiện đặc biệt.Tôi nghĩ rằng đây chính xác là những gì bạn đang tìm kiếm: https://github.com/JonasGessner/JGMethodSwizzler

Nó hoạt động bằng cách tạo một phân lớp động cho trường hợp cụ thể mà bạn đang bị vướng vào thời gian chạy.

Các vấn đề liên quan