2013-03-07 37 views
5

Tôi chỉ cấu hình một CABasicAnimation cho tài sản shadowPath và điều này khiến tôi tò mò:Cầu đúc yêu cầu, cảnh báo không phù hợp trong khi cố gắng sử dụng CGPathRef

shadowAnimation.toValue = (id)newShadowPath.CGPath; 

[lưu ý: shadowAnimation được một đối tượng CABasicAnimationnewShadowPath là một UIBezierPath đối tượng]

tác phẩm này và không có lỗi/cảnh báo nào hiển thị trong Xcode. Tuy nhiên, nếu tôi viết rằng như vậy:

CGPathRef test = newShadowPath.CGPath; 
shadowAnimation.toValue = (id)test; 

này sẽ không biên dịch, ném thông điệp cảnh báo này:

Cast of C pointer type 'CGPathRef' (aka 'const struct CGPath *') to Objective-C pointer type 'id' requires a bridged cast 

Vì vậy, nó đòi hỏi tôi phải gõ nó thích:

shadowAnimation.toValue = (__bridge id)test; 

Bây giờ tại sao cái này rất? Tại sao tôi không gặp lỗi tương tự trong ví dụ ban đầu khi tôi chỉ sử dụng (id) newShadowPath.CGPath; ? Nó sẽ được chính xác để đặt __bridge cast có quá bất kể Xcode không phát hiện bất kỳ vấn đề? Hay tôi bỏ lỡ sự khác biệt ở đây là gì?

Trả lời

2

Lý do cho sự khác biệt nằm trong quy tắc chuyển đổi mới (và một chút phức tạp) được giới thiệu trong tiếng kêu 3.1.

Thứ nhất, cho phép đun sôi nó xuống:

@@interface Foo : NSObject 
+ (CGPathRef)bar; 
@end 

CGPathRef foo(); 

void test() 
{ 
    // works without bridged cast: 
    id a = (id)[Foo bar]; 

    // needs bridged cast 
    id b = (__bridge id)foo(); 
} 

Vì vậy, khi đúc kết quả của một thông điệp chúng ta có thể bỏ qua các diễn viên brdge trong khi một chức năng cuộc gọi bình thường cần đến nó. Điều đó có vẻ lạ.

Lý do cho sự khác biệt nằm ở cách ARC diễn giải các hàm và tên phương thức và giả định giả định về số lượng giữ lại được gọi là C retainable pointer types (đối tượng nền tảng lõi).

Trong section 3.3.2 của hướng dẫn ARC ("Chuyển đổi sang retainable đối tượng kiểu con trỏ của biểu thức với ngữ nghĩa được biết đến"), bạn sẽ tìm thấy lý do cho sự khác biệt:

Một biểu thức được biết unretained nếu nó là một rvalue của C loại con trỏ có thể lưu lại và nó là [...] một tin nhắn gửi [...]

Nếu toán hạng dàn diễn viên được biết đến unretained [...] chuyển đổi được coi là một __bridge đúc

Phần tương tự mô tả tại sao điều này không áp dụng cho các hàm C và cách chúng có thể được trang trí để cho phép clang tạo ra các giả định tương tự. Vì vậy, chúng ta có thể sửa đổi mẫu từ trên cao để thoát khỏi các diễn viên cầu nối cho các cuộc gọi chức năng C, cũng như:

CGPathRef foo() __attribute__((cf_returns_not_retained)); 

Để trả lời câu hỏi cuối cùng của bạn: Đó là an toàn để sử dụng các diễn viên cầu ở cả hai nơi. Nó thậm chí làm cho chắc chắn ARC chọn quyền __bridge cast (nó có thể chọn các __bridge_transfer cast tùy thuộc vào tên của phương pháp. Trong trường hợp này nó sẽ sử dụng __bridge, mặc dù).

+0

wow, cảm ơn vì đã nỗ lực để phân tích điều này. Tôi đã chấp nhận điều này như là câu trả lời đúng bởi vì nó đi vào chiều dài lớn này để giải thích chính xác những gì đang xảy ra. –

2

Điều này là do ARC và sự kỳ diệu xảy ra khi các bài tập diễn ra.

Khi bạn tạo biến tạm thời CGPathRef test và gán cho nó, ARC thấy rằng bạn đang sử dụng phiên bản CoreBlank không theo mô hình bộ nhớ tiêu chuẩn và không giữ lại hoặc làm bất kỳ điều gì lạ mắt.

Sau đó, khi bạn cố gán phiên bản không được quản lý này cho một thuộc tính được giữ nguyên id, LLVM sẽ bị hỏng, vì nó cho rằng nó không phải quản lý bộ nhớ cho trường hợp đó, nhưng bây giờ nó sẽ bắt đầu đến (đó là ý định của từ khóa __bridge).

Trong đoạn mã đầu tiên, lý do nó hoạt động là vì ARC luôn biết rằng nó sẽ xử lý đối tượng đó như được ARC quản lý và không bao giờ tồn tại một tham chiếu đến nó không được quản lý ARC.

+0

Tôi không thấy cách giải thích này làm cho bất kỳ điều gì rõ ràng hơn. Tại sao 'id a = (id) [UIBezierPath mới];' công việc, nhưng 'id b = (__bridge id) CGPathCreateCopy (NULL);' cần cầu đúc? –

+2

@NikolaiRuhe 'UIBezierPath' là một lớp được quản lý ARC, trong đó nó không phải là một con trỏ C, nó là một con trỏ Objective-C. 'CGPathCreateCopy' trả về một con trỏ cấu trúc C, mà ARC không quản lý trừ khi được kết nối. –

+0

Xin lỗi vì sự nhầm lẫn: Tôi có nghĩa là 'id a = (id) [UIBezierPath mới] .CGPath;'. Vì vậy, sự khác biệt giữa hai trường hợp chỉ là 'a' nhận được kết quả của một thông điệp objc trong khi' b' nhận được kết quả của một hàm C. Tôi không hiểu tại sao ARC nên xử lý các trường hợp này một cách khác nhau. –

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