MetaType
Trong Swift, nếu Type được dịch theo nghĩa đen là kiểu của một biến, thì MetaType là kiểu của kiểu biến. Ví dụ, số 5 có kiểu là Int
, hay một biến Int
có thể nhận giá trị là 5. Tuy nhiên, để viết một phương thức trả về dung lượng bộ nhớ mà một kiểu biến chiếm dụng, chúng ta không thể truyền vào giá trị của biến mà cần truyền kiểu của kiểu biến: MetaType.
.Type
và .self
Trong ngôn ngữ Swift, MetaType được biểu thị qua thuộc tính .Type
. Mỗi biến đều có thể mang giá trị: nếu như biến kiểu Int
có thể mang giá trị 5, thì giá trị mà biến kiểu của kiểu Int
có thể mang giá trị được biểu thị qua thuộc tính .self
. Ví dụ, Int.Type
là kiểu của kiểu Int
, mang giá trị Int.self
.
1 2 3 | <span class="token keyword">let</span> int<span class="token punctuation">:</span> <span class="token builtin">Int</span> <span class="token operator">=</span> <span class="token number">5</span> <span class="token keyword">let</span> intMetaType<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token operator">=</span> <span class="token builtin">Int</span><span class="token punctuation">.</span><span class="token keyword">self</span> |
AnyClass
Thuộc tính và phương thức static có thể được truy cập và sử dụng sau khi đã lấy được MetaType. Trên thực tế, chúng ta thường xuyên sử dụng MetaType, ví dụ một phương thức rất phổ biến của UITableView
:
1 2 3 4 | <span class="token keyword">func</span> <span class="token function">register</span><span class="token punctuation">(</span><span class="token builtin">AnyClass</span><span class="token operator">?</span><span class="token punctuation">,</span> forCellReuseIdentifier<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> tableView<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token builtin">UITableViewCell</span><span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">,</span> forCellReuseIdentifier<span class="token punctuation">:</span> <span class="token string">"cell"</span><span class="token punctuation">)</span> |
Ở đây, AnyClass
thực ra là một MetaType:
1 2 | <span class="token keyword">typealias</span> <span class="token builtin">AnyClass</span> <span class="token operator">=</span> <span class="token builtin">AnyObject</span><span class="token punctuation">.</span><span class="token keyword">Type</span> |
Khi chúng ta truy cập đến thuộc tính static, thật ra là chúng ta đã truy cập thông qua MetaType, tuy nhiên XCode đã khéo léo giấu chúng đi bằng cách lược bỏ .self
. Hai cách viết sau là tương đương:
1 2 3 | <span class="token builtin">Int</span><span class="token punctuation">.</span><span class="token builtin">max</span> <span class="token builtin">Int</span><span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">.</span><span class="token builtin">max</span> |
type(of:)
và .self
Chúng ta đều có thể dùng type(of:)
và .self
để lấy giá trị cho MetaType:
1 2 3 | <span class="token keyword">let</span> instanceMetaType<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token operator">=</span> <span class="token function">type</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> <span class="token string">"string"</span><span class="token punctuation">)</span> <span class="token keyword">let</span> staticMetaType<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token operator">=</span> <span class="token builtin">String</span><span class="token punctuation">.</span><span class="token keyword">self</span> |
Điểm khác biệt là hàm type(of:)
được gọi trên biến, còn .self
được gọi trên kiểu của biến.
Protocol
Protocol không phải là một kiểu trong Swift, vì thế không thể trực tiếp truy cập giá trị cho kiểu của Protocol thông qua .self
mà phải thông qua class hay struct tương thích với Protocol đó:
1 2 3 | <span class="token keyword">struct</span> <span class="token builtin">MyType</span><span class="token punctuation">:</span> <span class="token builtin">MyProtocol</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> metatype<span class="token punctuation">:</span> <span class="token builtin">MyProtocol</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token operator">=</span> <span class="token builtin">MyType</span><span class="token punctuation">.</span><span class="token keyword">self</span> |
Vậy thì .self
trên Protocol có ý nghĩa gì? Đó chính là giá trị của kiểu Protocol của Protocol:
1 2 | <span class="token keyword">let</span> protocolMetatype<span class="token punctuation">:</span> <span class="token builtin">MyProtocol</span><span class="token punctuation">.</span><span class="token builtin">Protocol</span> <span class="token operator">=</span> <span class="token builtin">MyProtocol</span><span class="token punctuation">.</span><span class="token keyword">self</span> |
Ví dụ
Giả sử chúng ta có 2 class khác nhau cùng tương thích một protocol. Chúng ta cần viết hàm khởi tạo object dựa trên kiểu của class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">protocol</span> <span class="token builtin">ContentCell</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">IntCell</span><span class="token punctuation">:</span> <span class="token builtin">UIView</span><span class="token punctuation">,</span> <span class="token builtin">ContentCell</span> <span class="token punctuation">{</span> <span class="token keyword">required</span> <span class="token keyword">init</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>frame<span class="token punctuation">:</span> <span class="token builtin">CGRect</span><span class="token punctuation">.</span>zero<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">required</span> <span class="token keyword">init</span><span class="token operator">?</span><span class="token punctuation">(</span>coder aDecoder<span class="token punctuation">:</span> <span class="token builtin">NSCoder</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fatalError</span><span class="token punctuation">(</span><span class="token string">"init(coder:) has not been implemented"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">StringCell</span><span class="token punctuation">:</span> <span class="token builtin">UIView</span><span class="token punctuation">,</span> <span class="token builtin">ContentCell</span> <span class="token punctuation">{</span> <span class="token keyword">required</span> <span class="token keyword">init</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>frame<span class="token punctuation">:</span> <span class="token builtin">CGRect</span><span class="token punctuation">.</span>zero<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">required</span> <span class="token keyword">init</span><span class="token operator">?</span><span class="token punctuation">(</span>coder aDecoder<span class="token punctuation">:</span> <span class="token builtin">NSCoder</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fatalError</span><span class="token punctuation">(</span><span class="token string">"init(coder:) has not been implemented"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Hàm khởi tạo có thể viết theo cách truyền trực tiếp MetaType:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">func</span> <span class="token function">createCell</span><span class="token punctuation">(</span>type<span class="token punctuation">:</span> <span class="token builtin">ContentCell</span><span class="token punctuation">.</span><span class="token keyword">Type</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">ContentCell</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token keyword">let</span> intCell <span class="token operator">=</span> type <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">IntCell</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> intCell<span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token keyword">let</span> stringCell <span class="token operator">=</span> type <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">StringCell</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> stringCell<span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token string">"xx"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> intCell <span class="token operator">=</span> <span class="token function">createCell</span><span class="token punctuation">(</span>type<span class="token punctuation">:</span> <span class="token builtin">IntCell</span><span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">)</span> |
Hoặc có thể viết theo cách sử dụng generics:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">func</span> createCell<span class="token operator"><</span>T<span class="token punctuation">:</span> <span class="token builtin">ContentCell</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> T<span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token keyword">let</span> intCell <span class="token operator">=</span> T<span class="token punctuation">.</span><span class="token keyword">self</span> <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">IntCell</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> intCell<span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token keyword">as</span><span class="token operator">?</span> T <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token keyword">let</span> stringCell <span class="token operator">=</span> T<span class="token punctuation">.</span><span class="token keyword">self</span> <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">StringCell</span><span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> stringCell<span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token string">"xx"</span><span class="token punctuation">)</span> <span class="token keyword">as</span><span class="token operator">?</span> T <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token comment">// Now infer the metatype you need to use based on the return type</span> <span class="token keyword">let</span> stringCell<span class="token punctuation">:</span> <span class="token builtin">StringCell</span><span class="token operator">?</span> <span class="token operator">=</span> <span class="token function">createCell</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
Thư viện Reusable sử dụng cách tương tự cho phương thức dequeueReusableCell
:
1 2 3 4 5 6 7 8 | <span class="token keyword">func</span> dequeueReusableCell<span class="token operator"><</span>T<span class="token punctuation">:</span> <span class="token builtin">UITableViewCell</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">for</span> indexPath<span class="token punctuation">:</span> <span class="token builtin">IndexPath</span><span class="token punctuation">,</span> cellType<span class="token punctuation">:</span> T<span class="token punctuation">.</span><span class="token keyword">Type</span> <span class="token operator">=</span> T<span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> T <span class="token keyword">where</span> T<span class="token punctuation">:</span> <span class="token builtin">Reusable</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> cell <span class="token operator">=</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token function">dequeueReusableCell</span><span class="token punctuation">(</span>withIdentifier<span class="token punctuation">:</span> cellType<span class="token punctuation">.</span>reuseIdentifier<span class="token punctuation">,</span> <span class="token keyword">for</span><span class="token punctuation">:</span> indexPath<span class="token punctuation">)</span> <span class="token keyword">as</span><span class="token operator">?</span> T <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">fatalError</span><span class="token punctuation">(</span><span class="token string">"Failed to dequeue a cell"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> cell <span class="token punctuation">}</span> |